summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rwxr-xr-x[-rw-r--r--]indra/llcommon/CMakeLists.txt161
-rw-r--r--indra/llcommon/bitpack.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/ctype_workaround.h36
-rw-r--r--indra/llcommon/doublelinkedlist.h1403
-rwxr-xr-xindra/llcommon/fix_macros.h25
-rw-r--r--indra/llcommon/imageids.cpp76
-rw-r--r--indra/llcommon/imageids.h75
-rwxr-xr-x[-rw-r--r--]indra/llcommon/indra_constants.cpp67
-rwxr-xr-x[-rw-r--r--]indra/llcommon/indra_constants.h384
-rwxr-xr-x[-rw-r--r--]indra/llcommon/is_approx_equal_fraction.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/linden_common.h59
-rw-r--r--indra/llcommon/linked_lists.h943
-rw-r--r--indra/llcommon/ll_template_cast.h160
-rw-r--r--indra/llcommon/llagentconstants.h165
-rw-r--r--indra/llcommon/llalignedarray.h139
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llallocator.cpp71
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llallocator.h42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llallocator_heap_profile.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llallocator_heap_profile.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llapp.cpp580
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llapp.h122
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llapr.cpp106
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llapr.h93
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llassettype.cpp125
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llassettype.h52
-rw-r--r--indra/llcommon/llassoclist.h302
-rw-r--r--indra/llcommon/llavatarconstants.h61
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llbase32.cpp40
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llbase32.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llbase64.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llbase64.h36
-rwxr-xr-xindra/llcommon/llbitpack.cpp30
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llbitpack.h (renamed from indra/llcommon/bitpack.h)48
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llboost.h36
-rw-r--r--indra/llcommon/llchat.h113
-rw-r--r--indra/llcommon/llclickaction.h47
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcommon.cpp53
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcommon.h36
-rwxr-xr-xindra/llcommon/llcommonutils.cpp56
-rwxr-xr-xindra/llcommon/llcommonutils.h53
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcoros.cpp40
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcoros.h33
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcrc.cpp40
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcrc.h38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcriticaldamp.cpp114
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llcriticaldamp.h68
-rw-r--r--indra/llcommon/llcursortypes.cpp91
-rw-r--r--indra/llcommon/llcursortypes.h79
-rw-r--r--indra/llcommon/lldarray.h229
-rw-r--r--indra/llcommon/lldarrayptr.h42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldate.cpp90
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldate.h41
-rw-r--r--indra/llcommon/lldeadmantimer.cpp188
-rw-r--r--indra/llcommon/lldeadmantimer.h214
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldefs.h44
-rw-r--r--indra/llcommon/lldeleteutils.h53
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldependencies.cpp21
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldependencies.h21
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldepthstack.h75
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldictionary.cpp35
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lldictionary.h43
-rw-r--r--indra/llcommon/lldlinked.h99
-rwxr-xr-xindra/llcommon/lldoubledispatch.h36
-rw-r--r--indra/llcommon/lldqueueptr.h358
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llendianswizzle.h36
-rw-r--r--indra/llcommon/llenum.h84
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerror.cpp857
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerror.h298
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerrorcontrol.h144
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerrorlegacy.h122
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerrorthread.cpp132
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llerrorthread.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llevent.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llevent.h38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventapi.cpp51
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventapi.h105
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventcoro.cpp21
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventcoro.h45
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventdispatcher.cpp587
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventdispatcher.h414
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventemitter.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventfilter.cpp21
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventfilter.h21
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llevents.cpp54
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llevents.h37
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventtimer.cpp56
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lleventtimer.h39
-rw-r--r--indra/llcommon/llextendedstatus.h73
-rwxr-xr-xindra/llcommon/llfasttimer.cpp491
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfasttimer.h432
-rw-r--r--indra/llcommon/llfasttimer_class.cpp768
-rw-r--r--indra/llcommon/llfasttimer_class.h272
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfile.cpp840
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfile.h429
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfindlocale.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfindlocale.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfixedbuffer.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llfixedbuffer.h36
-rw-r--r--indra/llcommon/llfoldertype.cpp169
-rw-r--r--indra/llcommon/llfoldertype.h112
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llformat.cpp68
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llformat.h42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llframetimer.cpp45
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llframetimer.h41
-rwxr-xr-xindra/llcommon/llhandle.h216
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llhash.h79
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llheartbeat.cpp44
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llheartbeat.h36
-rw-r--r--indra/llcommon/llhttpstatuscodes.h95
-rw-r--r--indra/llcommon/llindexedqueue.h161
-rwxr-xr-xindra/llcommon/llindexedvector.h100
-rwxr-xr-xindra/llcommon/llinitparam.cpp487
-rwxr-xr-xindra/llcommon/llinitparam.h2839
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llinstancetracker.cpp42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llinstancetracker.h377
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llkeythrottle.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llkeyusetracker.h36
-rw-r--r--indra/llcommon/lllazy.cpp23
-rw-r--r--indra/llcommon/lllazy.h382
-rwxr-xr-xindra/llcommon/llleap.cpp459
-rwxr-xr-xindra/llcommon/llleap.h81
-rwxr-xr-xindra/llcommon/llleaplistener.cpp287
-rwxr-xr-xindra/llcommon/llleaplistener.h73
-rw-r--r--indra/llcommon/lllinkedqueue.h315
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lllistenerwrapper.h21
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llliveappconfig.cpp48
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llliveappconfig.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lllivefile.cpp121
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lllivefile.h36
-rw-r--r--indra/llcommon/lllocalidhashmap.h901
-rw-r--r--indra/llcommon/lllog.cpp117
-rw-r--r--indra/llcommon/lllog.h58
-rw-r--r--indra/llcommon/lllslconstants.h219
-rw-r--r--indra/llcommon/llmap.h251
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmd5.cpp88
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmd5.h47
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmemory.cpp2063
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmemory.h694
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmemorystream.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmemorystream.h36
-rw-r--r--indra/llcommon/llmemtype.cpp237
-rw-r--r--indra/llcommon/llmemtype.h248
-rwxr-xr-xindra/llcommon/llmetricperformancetester.cpp335
-rwxr-xr-xindra/llcommon/llmetricperformancetester.h215
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmetrics.cpp40
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmetrics.h38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmortician.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llmortician.h37
-rw-r--r--indra/llcommon/llmutex.cpp205
-rw-r--r--indra/llcommon/llmutex.h141
-rw-r--r--indra/llcommon/llnametable.h36
-rw-r--r--indra/llcommon/lloptioninterface.cpp39
-rw-r--r--indra/llcommon/lloptioninterface.h46
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llpointer.h225
-rw-r--r--indra/llcommon/llpredicate.cpp41
-rw-r--r--indra/llcommon/llpredicate.h210
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llpreprocessor.h66
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llpriqueuemap.h42
-rwxr-xr-xindra/llcommon/llprocess.cpp1351
-rwxr-xr-xindra/llcommon/llprocess.h553
-rw-r--r--indra/llcommon/llprocesslauncher.cpp344
-rw-r--r--indra/llcommon/llprocesslauncher.h93
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llprocessor.cpp2828
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llprocessor.h205
-rw-r--r--indra/llcommon/llprocinfo.cpp94
-rw-r--r--indra/llcommon/llprocinfo.h68
-rw-r--r--indra/llcommon/llptrskiplist.h730
-rw-r--r--indra/llcommon/llptrskipmap.h1245
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llptrto.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llptrto.h37
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llqueuedthread.cpp86
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llqueuedthread.h44
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llrand.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llrand.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llrefcount.cpp148
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llrefcount.h144
-rwxr-xr-xindra/llcommon/llregistry.h358
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llrun.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llrun.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsafehandle.h38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsd.cpp270
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsd.h175
-rwxr-xr-xindra/llcommon/llsdparam.cpp344
-rwxr-xr-xindra/llcommon/llsdparam.h143
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsdserialize.cpp446
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsdserialize.h84
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsdserialize_xml.cpp89
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsdserialize_xml.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsdutil.cpp65
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsdutil.h323
-rw-r--r--indra/llcommon/llsecondlifeurls.cpp90
-rw-r--r--indra/llcommon/llsecondlifeurls.h82
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsimplehash.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsingleton.cpp37
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsingleton.h217
-rw-r--r--indra/llcommon/llskiplist.h523
-rw-r--r--indra/llcommon/llskipmap.h1028
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsmoothstep.h36
-rw-r--r--indra/llcommon/llstack.h54
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstacktrace.cpp67
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstacktrace.h37
-rw-r--r--indra/llcommon/llstat.cpp1312
-rw-r--r--indra/llcommon/llstat.h359
-rw-r--r--indra/llcommon/llstatenums.h75
-rw-r--r--indra/llcommon/llstaticstringtable.h82
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstl.h288
-rwxr-xr-xindra/llcommon/llstreamqueue.cpp24
-rwxr-xr-xindra/llcommon/llstreamqueue.h240
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstreamtools.cpp36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstreamtools.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstrider.h48
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstring.cpp290
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstring.h688
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstringtable.cpp40
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llstringtable.h44
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsys.cpp1079
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llsys.h76
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llthread.cpp291
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llthread.h185
-rw-r--r--indra/llcommon/llthreadlocalstorage.cpp119
-rw-r--r--indra/llcommon/llthreadlocalstorage.h148
-rwxr-xr-xindra/llcommon/llthreadsafequeue.cpp109
-rwxr-xr-xindra/llcommon/llthreadsafequeue.h205
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lltimer.cpp168
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lltimer.h92
-rw-r--r--indra/llcommon/lltrace.cpp86
-rw-r--r--indra/llcommon/lltrace.h492
-rw-r--r--indra/llcommon/lltraceaccumulators.cpp302
-rw-r--r--indra/llcommon/lltraceaccumulators.h602
-rw-r--r--indra/llcommon/lltracerecording.cpp1251
-rw-r--r--indra/llcommon/lltracerecording.h670
-rw-r--r--indra/llcommon/lltracethreadrecorder.cpp329
-rw-r--r--indra/llcommon/lltracethreadrecorder.h102
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lltreeiterators.h36
-rw-r--r--indra/llcommon/llunits.h129
-rw-r--r--indra/llcommon/llunittype.h838
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lluri.cpp116
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lluri.h36
-rw-r--r--indra/llcommon/lluriparser.cpp261
-rw-r--r--indra/llcommon/lluriparser.h87
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lluuid.cpp348
-rwxr-xr-x[-rw-r--r--]indra/llcommon/lluuid.h225
-rw-r--r--indra/llcommon/lluuidhashmap.h589
-rw-r--r--indra/llcommon/llversionserver.h44
-rw-r--r--indra/llcommon/llversionviewer.h43
-rw-r--r--indra/llcommon/llwin32headers.h42
-rw-r--r--indra/llcommon/llwin32headerslean.h40
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llworkerthread.cpp68
-rwxr-xr-x[-rw-r--r--]indra/llcommon/llworkerthread.h45
-rw-r--r--indra/llcommon/metaclass.cpp87
-rw-r--r--indra/llcommon/metaclass.h88
-rw-r--r--indra/llcommon/metaclasst.h66
-rw-r--r--indra/llcommon/metaproperty.cpp62
-rw-r--r--indra/llcommon/metaproperty.h79
-rw-r--r--indra/llcommon/metapropertyt.h189
-rw-r--r--indra/llcommon/reflective.cpp46
-rw-r--r--indra/llcommon/reflective.h48
-rw-r--r--indra/llcommon/reflectivet.h54
-rw-r--r--indra/llcommon/roles_constants.h197
-rw-r--r--indra/llcommon/stdenums.h140
-rwxr-xr-x[-rw-r--r--]indra/llcommon/stdtypes.h36
-rw-r--r--indra/llcommon/string_table.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/stringize.h78
-rwxr-xr-xindra/llcommon/tests/StringVec.h37
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/bitpack_test.cpp55
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/commonmisc_test.cpp52
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/listener.h42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llallocator_heap_profile_test.cpp42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llallocator_test.cpp42
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llbase64_test.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lldate_test.cpp38
-rw-r--r--indra/llcommon/tests/lldeadmantimer_test.cpp628
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lldependencies_test.cpp81
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llerror_test.cpp411
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lleventcoro_test.cpp79
-rwxr-xr-xindra/llcommon/tests/lleventdispatcher_test.cpp1324
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lleventfilter_test.cpp25
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llframetimer_test.cpp71
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llinstancetracker_test.cpp176
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lllazy_test.cpp21
-rwxr-xr-xindra/llcommon/tests/llleap_test.cpp671
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llmemtype_test.cpp42
-rwxr-xr-xindra/llcommon/tests/llprocess_test.cpp1262
-rwxr-xr-xindra/llcommon/tests/llprocessor_test.cpp61
-rw-r--r--indra/llcommon/tests/llprocinfo_test.cpp91
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llrand_test.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llsdserialize_test.cpp310
-rwxr-xr-xindra/llcommon/tests/llsingleton_test.cpp76
-rwxr-xr-xindra/llcommon/tests/llstreamqueue_test.cpp197
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/llstring_test.cpp166
-rw-r--r--indra/llcommon/tests/lltrace_test.cpp142
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lltreeiterators_test.cpp38
-rw-r--r--indra/llcommon/tests/llunits_test.cpp388
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/lluri_test.cpp130
-rw-r--r--indra/llcommon/tests/reflection_test.cpp226
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/stringize_test.cpp58
-rwxr-xr-x[-rw-r--r--]indra/llcommon/tests/wrapllerrs.h167
-rwxr-xr-x[-rw-r--r--]indra/llcommon/timer.h36
-rwxr-xr-x[-rw-r--r--]indra/llcommon/timing.cpp36
-rw-r--r--indra/llcommon/timing.h48
-rwxr-xr-x[-rw-r--r--]indra/llcommon/u64.cpp38
-rwxr-xr-x[-rw-r--r--]indra/llcommon/u64.h36
302 files changed, 36781 insertions, 25869 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 4481d334b2..1459b9ada2 100644..100755
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -7,16 +7,19 @@ include(00-Common)
include(LLCommon)
include(Linking)
include(Boost)
-include(Pth)
include(LLSharedLibs)
+include(GoogleBreakpad)
include(GooglePerfTools)
include(Copy3rdPartyLibs)
+include(ZLIB)
+include(URIPARSER)
include_directories(
${EXPAT_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
- ${PTH_INCLUDE_DIRS}
+ ${BREAKPAD_INCLUDE_DIRECTORIES}
+ ${URIPARSER_INCLUDE_DIRS}
)
# add_executable(lltreeiterators lltreeiterators.cpp)
@@ -25,7 +28,6 @@ include_directories(
# ${LLCOMMON_LIBRARIES})
set(llcommon_SOURCE_FILES
- imageids.cpp
indra_constants.cpp
llallocator.cpp
llallocator_heap_profile.cpp
@@ -34,12 +36,14 @@ set(llcommon_SOURCE_FILES
llassettype.cpp
llbase32.cpp
llbase64.cpp
+ llbitpack.cpp
llcommon.cpp
+ llcommonutils.cpp
llcoros.cpp
llcrc.cpp
llcriticaldamp.cpp
- llcursortypes.cpp
lldate.cpp
+ lldeadmantimer.cpp
lldependencies.cpp
lldictionary.cpp
llerror.cpp
@@ -51,51 +55,59 @@ set(llcommon_SOURCE_FILES
lleventfilter.cpp
llevents.cpp
lleventtimer.cpp
- llfasttimer_class.cpp
+ llfasttimer.cpp
llfile.cpp
llfindlocale.cpp
llfixedbuffer.cpp
- llfoldertype.cpp
llformat.cpp
llframetimer.cpp
llheartbeat.cpp
+ llinitparam.cpp
+ llinstancetracker.cpp
+ llleap.cpp
+ llleaplistener.cpp
llliveappconfig.cpp
lllivefile.cpp
- lllog.cpp
llmd5.cpp
llmemory.cpp
llmemorystream.cpp
- llmemtype.cpp
llmetrics.cpp
+ llmetricperformancetester.cpp
llmortician.cpp
- lloptioninterface.cpp
+ llmutex.cpp
llptrto.cpp
- llprocesslauncher.cpp
+ llpredicate.cpp
+ llprocess.cpp
llprocessor.cpp
+ llprocinfo.cpp
llqueuedthread.cpp
llrand.cpp
llrefcount.cpp
llrun.cpp
llsd.cpp
+ llsdparam.cpp
llsdserialize.cpp
llsdserialize_xml.cpp
llsdutil.cpp
- llsecondlifeurls.cpp
llsingleton.cpp
- llstat.cpp
llstacktrace.cpp
+ llstreamqueue.cpp
llstreamtools.cpp
llstring.cpp
llstringtable.cpp
llsys.cpp
llthread.cpp
+ llthreadlocalstorage.cpp
+ llthreadsafequeue.cpp
lltimer.cpp
+ lltrace.cpp
+ lltraceaccumulators.cpp
+ lltracerecording.cpp
+ lltracethreadrecorder.cpp
lluri.cpp
+ lluriparser.cpp
lluuid.cpp
llworkerthread.cpp
- metaclass.cpp
- metaproperty.cpp
- reflective.cpp
timing.cpp
u64.cpp
)
@@ -103,47 +115,35 @@ set(llcommon_SOURCE_FILES
set(llcommon_HEADER_FILES
CMakeLists.txt
- bitpack.h
ctype_workaround.h
- doublelinkedlist.h
- imageids.h
+ fix_macros.h
indra_constants.h
linden_common.h
- linked_lists.h
+ llalignedarray.h
llallocator.h
llallocator_heap_profile.h
- llagentconstants.h
llapp.h
llapr.h
llassettype.h
- llassoclist.h
- llavatarconstants.h
llbase32.h
llbase64.h
+ llbitpack.h
llboost.h
- llchat.h
- llclickaction.h
llcommon.h
+ llcommonutils.h
llcoros.h
llcrc.h
llcriticaldamp.h
- llcursortypes.h
- lldarray.h
- lldarrayptr.h
lldate.h
+ lldeadmantimer.h
lldefs.h
lldependencies.h
- lldeleteutils.h
lldepthstack.h
lldictionary.h
- lldlinked.h
lldoubledispatch.h
- lldqueueptr.h
llendianswizzle.h
- llenum.h
llerror.h
llerrorcontrol.h
- llerrorlegacy.h
llerrorthread.h
llevent.h
lleventapi.h
@@ -152,94 +152,82 @@ set(llcommon_HEADER_FILES
lleventfilter.h
llevents.h
lleventemitter.h
- llextendedstatus.h
llfasttimer.h
llfile.h
llfindlocale.h
llfixedbuffer.h
- llfoldertype.h
llformat.h
llframetimer.h
+ llhandle.h
llhash.h
llheartbeat.h
- llhttpstatuscodes.h
- llindexedqueue.h
+ llindexedvector.h
+ llinitparam.h
llinstancetracker.h
llkeythrottle.h
- lllazy.h
+ llleap.h
+ llleaplistener.h
lllistenerwrapper.h
- lllinkedqueue.h
llliveappconfig.h
lllivefile.h
- lllocalidhashmap.h
- lllog.h
- lllslconstants.h
- llmap.h
llmd5.h
llmemory.h
llmemorystream.h
- llmemtype.h
llmetrics.h
+ llmetricperformancetester.h
llmortician.h
llnametable.h
- lloptioninterface.h
llpointer.h
+ llpredicate.h
llpreprocessor.h
llpriqueuemap.h
- llprocesslauncher.h
+ llprocess.h
llprocessor.h
- llptrskiplist.h
- llptrskipmap.h
+ llprocinfo.h
llptrto.h
llqueuedthread.h
llrand.h
llrefcount.h
+ llregistry.h
llrun.h
llrefcount.h
llsafehandle.h
llsd.h
+ llsdparam.h
llsdserialize.h
llsdserialize_xml.h
llsdutil.h
- llsecondlifeurls.h
llsimplehash.h
llsingleton.h
- llskiplist.h
- llskipmap.h
- llstack.h
llstacktrace.h
- llstat.h
- llstatenums.h
llstl.h
+ llstreamqueue.h
llstreamtools.h
llstrider.h
llstring.h
llstringtable.h
+ llstaticstringtable.h
llsys.h
llthread.h
+ llthreadlocalstorage.h
+ llthreadsafequeue.h
lltimer.h
+ lltrace.h
+ lltraceaccumulators.h
+ lltracerecording.h
+ lltracethreadrecorder.h
lltreeiterators.h
+ llunits.h
+ llunittype.h
lluri.h
+ lluriparser.h
lluuid.h
- lluuidhashmap.h
- llversionserver.h
- llversionviewer.h
+ llwin32headers.h
+ llwin32headerslean.h
llworkerthread.h
- ll_template_cast.h
- metaclass.h
- metaclasst.h
- metaproperty.h
- metapropertyt.h
- processor.h
- reflective.h
- reflectivet.h
- roles_constants.h
- stdenums.h
stdtypes.h
- string_table.h
stringize.h
timer.h
- timing.h
u64.h
)
@@ -249,14 +237,26 @@ set_source_files_properties(${llcommon_HEADER_FILES}
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
if(LLCOMMON_LINK_SHARED)
- add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
- ll_stage_sharedlib(llcommon)
+ add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
+ if(NOT WORD_SIZE EQUAL 32)
+ if(WINDOWS)
+ add_definitions(/FIXED:NO)
+ else(WINDOWS) # not windows therefore gcc LINUX and DARWIN
+ add_definitions(-fPIC)
+ endif(WINDOWS)
+ endif(NOT WORD_SIZE EQUAL 32)
+ if(WINDOWS)
+ # always generate llcommon.pdb, even for "Release" builds
+ set_target_properties(llcommon PROPERTIES LINK_FLAGS "/DEBUG")
+ endif(WINDOWS)
+ ll_stage_sharedlib(llcommon)
else(LLCOMMON_LINK_SHARED)
add_library (llcommon ${llcommon_SOURCE_FILES})
endif(LLCOMMON_LINK_SHARED)
target_link_libraries(
llcommon
+ ${BREAKPAD_EXCEPTION_HANDLER_LIBRARIES}
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
${EXPAT_LIBRARIES}
@@ -264,10 +264,16 @@ target_link_libraries(
${WINDOWS_LIBRARIES}
${BOOST_PROGRAM_OPTIONS_LIBRARY}
${BOOST_REGEX_LIBRARY}
- ${PTH_LIBRARIES}
${GOOGLE_PERFTOOLS_LIBRARIES}
+ ${URIPARSER_LIBRARIES}
)
+if (DARWIN)
+ include(CMakeFindFrameworks)
+ find_library(CARBON_LIBRARY Carbon)
+ target_link_libraries(llcommon ${CARBON_LIBRARY})
+endif (DARWIN)
+
add_dependencies(llcommon stage_third_party_libs)
if (LL_TESTS)
@@ -283,18 +289,27 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
- LL_ADD_INTEGRATION_TEST(lllazy "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lluri "" "${test_libs}")
- LL_ADD_INTEGRATION_TEST(reflection "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llunits "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(stringize "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs};${BOOST_CONTEXT_LIBRARY};${BOOST_THREAD_LIBRARY};${BOOST_COROUTINE_LIBRARY};${BOOST_SYSTEM_LIBRARY}")
+ LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)
diff --git a/indra/llcommon/bitpack.cpp b/indra/llcommon/bitpack.cpp
deleted file mode 100644
index 8db403febf..0000000000
--- a/indra/llcommon/bitpack.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @file bitpack.cpp
- * @brief LLBitPack class implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-// implementation is all in the header, this include dep ensures the unit test is rerun if the implementation changes.
-#include "bitpack.h"
diff --git a/indra/llcommon/ctype_workaround.h b/indra/llcommon/ctype_workaround.h
index 32ccb8680a..552be9fb90 100644..100755
--- a/indra/llcommon/ctype_workaround.h
+++ b/indra/llcommon/ctype_workaround.h
@@ -3,31 +3,25 @@
* @brief The workaround is to create some legacy symbols that point
* to the correct symbols, which avoids link errors.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/doublelinkedlist.h b/indra/llcommon/doublelinkedlist.h
deleted file mode 100644
index a87860cf3b..0000000000
--- a/indra/llcommon/doublelinkedlist.h
+++ /dev/null
@@ -1,1403 +0,0 @@
-/**
- * @file doublelinkedlist.h
- * @brief Provides a standard doubly linked list for fun and profit.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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 = ll_rand(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/fix_macros.h b/indra/llcommon/fix_macros.h
new file mode 100755
index 0000000000..ef959decff
--- /dev/null
+++ b/indra/llcommon/fix_macros.h
@@ -0,0 +1,25 @@
+/**
+ * @file fix_macros.h
+ * @author Nat Goodspeed
+ * @date 2012-11-16
+ * @brief The Mac system headers seem to #define macros with obnoxiously
+ * generic names, preventing any library from using those names. We've
+ * had to fix these in so many places that it's worth making a header
+ * file to handle it.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// DON'T use an #include guard: every time we encounter this header, #undef
+// these macros all over again.
+
+// who injects MACROS with such generic names?! Grr.
+#ifdef equivalent
+#undef equivalent
+#endif
+
+#ifdef check
+#undef check
+#endif
diff --git a/indra/llcommon/imageids.cpp b/indra/llcommon/imageids.cpp
deleted file mode 100644
index f48bb1374d..0000000000
--- a/indra/llcommon/imageids.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * @file imageids.cpp
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "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_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER
-
-const LLUUID IMG_DEFAULT ("d2114404-dd59-4a4d-8e6c-49359e91bbf0"); // VIEWER
-
-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_FACE_SELECT ("a85ac674-cb75-4af6-9499-df7c5aaf7a28"); // face selector
-const LLUUID IMG_DEFAULT_AVATAR ("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"); // dataserver
-const LLUUID IMG_INVISIBLE ("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"); // 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_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_BLOOM1 ("3c59f7fe-9dc8-47f9-8aaf-a9dd1fbc3bef"); // VIEWER
-const LLUUID TERRAIN_DIRT_DETAIL ("0bc58228-74a0-7e83-89bc-5c23464bcec5"); // VIEWER
-const LLUUID TERRAIN_GRASS_DETAIL ("63338ede-0037-c4fd-855b-015d77112fc8"); // VIEWER
-const LLUUID TERRAIN_MOUNTAIN_DETAIL ("303cd381-8560-7579-23f1-f0a880799740"); // VIEWER
-const LLUUID TERRAIN_ROCK_DETAIL ("53a2f406-4895-1d13-d541-d2e3b86bc19c"); // VIEWER
-
-const LLUUID DEFAULT_WATER_NORMAL ("822ded49-9a6c-f61c-cb89-6df54f42cdf4"); // VIEWER
diff --git a/indra/llcommon/imageids.h b/indra/llcommon/imageids.h
deleted file mode 100644
index dc726dcf53..0000000000
--- a/indra/llcommon/imageids.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * @file imageids.h
- * @brief Temporary holder for image IDs
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_IMAGEIDS_H
-#define LL_IMAGEIDS_H
-
-//
-// USE OF THIS FILE IS DEPRECATED
-//
-// Please use viewerart.ini and the standard
-// art import path.
-
-class LLUUID;
-
-LL_COMMON_API extern const LLUUID IMG_SMOKE;
-
-LL_COMMON_API extern const LLUUID IMG_DEFAULT;
-
-LL_COMMON_API extern const LLUUID IMG_SUN;
-LL_COMMON_API extern const LLUUID IMG_MOON;
-LL_COMMON_API extern const LLUUID IMG_CLOUD_POOF;
-LL_COMMON_API extern const LLUUID IMG_SHOT;
-LL_COMMON_API extern const LLUUID IMG_SPARK;
-LL_COMMON_API extern const LLUUID IMG_FIRE;
-LL_COMMON_API extern const LLUUID IMG_FACE_SELECT;
-LL_COMMON_API extern const LLUUID IMG_DEFAULT_AVATAR;
-LL_COMMON_API extern const LLUUID IMG_INVISIBLE;
-
-LL_COMMON_API extern const LLUUID IMG_EXPLOSION;
-LL_COMMON_API extern const LLUUID IMG_EXPLOSION_2;
-LL_COMMON_API extern const LLUUID IMG_EXPLOSION_3;
-LL_COMMON_API extern const LLUUID IMG_EXPLOSION_4;
-LL_COMMON_API extern const LLUUID IMG_SMOKE_POOF;
-
-LL_COMMON_API extern const LLUUID IMG_BIG_EXPLOSION_1;
-LL_COMMON_API extern const LLUUID IMG_BIG_EXPLOSION_2;
-
-LL_COMMON_API extern const LLUUID IMG_BLOOM1;
-LL_COMMON_API extern const LLUUID TERRAIN_DIRT_DETAIL;
-LL_COMMON_API extern const LLUUID TERRAIN_GRASS_DETAIL;
-LL_COMMON_API extern const LLUUID TERRAIN_MOUNTAIN_DETAIL;
-LL_COMMON_API extern const LLUUID TERRAIN_ROCK_DETAIL;
-
-LL_COMMON_API extern const LLUUID DEFAULT_WATER_NORMAL;
-
-#endif
diff --git a/indra/llcommon/indra_constants.cpp b/indra/llcommon/indra_constants.cpp
index 8a1290d4dc..f3989ee1d0 100644..100755
--- a/indra/llcommon/indra_constants.cpp
+++ b/indra/llcommon/indra_constants.cpp
@@ -2,31 +2,25 @@
* @file indra_constants.cpp
* @brief some useful short term constants for Indra
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
@@ -41,6 +35,35 @@ const LLUUID LL_UUID_ALL_AGENTS("44e87126-e794-4ded-05b3-7c42da3d5cdb");
// Governor Linden's agent id.
const LLUUID ALEXANDRIA_LINDEN_ID("ba2a564a-f0f1-4b82-9c61-b7520bfcd09f");
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");
+// Grass Images
+const LLUUID IMG_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER
+
+const LLUUID IMG_DEFAULT ("d2114404-dd59-4a4d-8e6c-49359e91bbf0"); // VIEWER
+
+const LLUUID IMG_SUN ("cce0f112-878f-4586-a2e2-a8f104bba271"); // dataserver
+const LLUUID IMG_MOON ("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // 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_FACE_SELECT ("a85ac674-cb75-4af6-9499-df7c5aaf7a28"); // face selector
+const LLUUID IMG_DEFAULT_AVATAR ("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"); // dataserver
+const LLUUID IMG_INVISIBLE ("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"); // 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_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_BLOOM1 ("3c59f7fe-9dc8-47f9-8aaf-a9dd1fbc3bef"); // VIEWER
+const LLUUID TERRAIN_DIRT_DETAIL ("0bc58228-74a0-7e83-89bc-5c23464bcec5"); // VIEWER
+const LLUUID TERRAIN_GRASS_DETAIL ("63338ede-0037-c4fd-855b-015d77112fc8"); // VIEWER
+const LLUUID TERRAIN_MOUNTAIN_DETAIL ("303cd381-8560-7579-23f1-f0a880799740"); // VIEWER
+const LLUUID TERRAIN_ROCK_DETAIL ("53a2f406-4895-1d13-d541-d2e3b86bc19c"); // VIEWER
+
+const LLUUID DEFAULT_WATER_NORMAL ("822ded49-9a6c-f61c-cb89-6df54f42cdf4"); // VIEWER
diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h
index d4a07d77cc..02f063f5e8 100644..100755
--- a/indra/llcommon/indra_constants.h
+++ b/indra/llcommon/indra_constants.h
@@ -2,31 +2,25 @@
* @file indra_constants.h
* @brief some useful short term constants for Indra
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,121 +31,28 @@
class LLUUID;
-// At 45 Hz collisions seem stable and objects seem
-// to settle down at a reasonable rate.
-// JC 3/18/2003
-
-// const F32 PHYSICS_TIMESTEP = 1.f / 45.f;
-// This must be a #define due to anal retentive restrictions on const expressions
-// CG 2008-06-05
-#define PHYSICS_TIMESTEP (1.f / 45.f)
-
-const F32 COLLISION_TOLERANCE = 0.1f;
-const F32 HALF_COLLISION_TOLERANCE = 0.05f;
-
-// 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;
-
static const F32 REGION_WIDTH_METERS = 256.f;
static const S32 REGION_WIDTH_UNITS = 256;
static const U32 REGION_WIDTH_U32 = 256;
const F32 REGION_HEIGHT_METERS = 4096.f;
-// 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;
-
-// Region absolute limits
-static const S32 REGION_AGENT_COUNT_MIN = 1;
-static const S32 REGION_AGENT_COUNT_MAX = 200; // Must fit in U8 for the moment (RegionInfo msg)
-static const S32 REGION_PRIM_COUNT_MIN = 0;
-static const S32 REGION_PRIM_COUNT_MAX = 40000;
-static const F32 REGION_PRIM_BONUS_MIN = 1.0;
-static const F32 REGION_PRIM_BONUS_MAX = 10.0;
-
-// Default maximum number of tasks/prims per region.
-const U32 DEFAULT_MAX_REGION_WIDE_PRIM_COUNT = 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.1f;
const F32 DEFAULT_AGENT_HEIGHT = 1.9f;
-const F32 MAX_AGENT_HEIGHT = 2.45f;
-
-// For linked sets
-const S32 MAX_CHILDREN_PER_TASK = 255;
-const S32 MAX_CHILDREN_PER_PHYSICAL_TASK = 32;
-
-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
-// https://wiki.lindenlab.com/wiki/Name_Space_Ports
-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; // Deprecated
-//const U32 DEFAULT_QUERYSIM_PORT = 12031; // Deprecated
-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; // Deprecated
-const U32 DEFAULT_CAP_PROXY_PORT = 12043;
-const U32 DEFAULT_INV_DATA_SERVER_PORT = 12044;
-const U32 DEFAULT_CGI_SERVICES_PORT = 12045;
-
-// Mapserver uses ports 12124 - 12139 to allow multiple mapservers to run
-// on a single host for map tile generation. JC
-const U32 DEFAULT_MAPSERVER_PORT = 12124;
-
-// 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';
+
+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
+};
// keys
// Bit masks for various keyboard modifier keys.
@@ -251,9 +152,6 @@ const U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only
const U8 SIM_ACCESS_DOWN = 254;
const U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT;
-// group constants
-const S32 MAX_AGENT_GROUPS = 25;
-
// attachment constants
const S32 MAX_AGENT_ATTACHMENTS = 38;
const U8 ATTACHMENT_ADD = 0x80;
@@ -273,73 +171,43 @@ LL_COMMON_API extern const LLUUID LL_UUID_ALL_AGENTS;
LL_COMMON_API extern const LLUUID ALEXANDRIA_LINDEN_ID;
LL_COMMON_API extern const LLUUID GOVERNOR_LINDEN_ID;
-LL_COMMON_API extern const LLUUID REALESTATE_LINDEN_ID;
// Maintenance's group id.
LL_COMMON_API extern const LLUUID MAINTENANCE_GROUP_ID;
-// 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;
-const U8 AU_FLAGS_CLIENT_AUTOPILOT = 0x02;
-
-// 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;
+// image ids
+LL_COMMON_API extern const LLUUID IMG_SMOKE;
-// 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;
+LL_COMMON_API extern const LLUUID IMG_DEFAULT;
-// this times above gives barely audible radius
-const F32 CHAT_BARELY_AUDIBLE_FACTOR = 2.0f;
+LL_COMMON_API extern const LLUUID IMG_SUN;
+LL_COMMON_API extern const LLUUID IMG_MOON;
+LL_COMMON_API extern const LLUUID IMG_SHOT;
+LL_COMMON_API extern const LLUUID IMG_SPARK;
+LL_COMMON_API extern const LLUUID IMG_FIRE;
+LL_COMMON_API extern const LLUUID IMG_FACE_SELECT;
+LL_COMMON_API extern const LLUUID IMG_DEFAULT_AVATAR;
+LL_COMMON_API extern const LLUUID IMG_INVISIBLE;
-// 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;
+LL_COMMON_API extern const LLUUID IMG_EXPLOSION;
+LL_COMMON_API extern const LLUUID IMG_EXPLOSION_2;
+LL_COMMON_API extern const LLUUID IMG_EXPLOSION_3;
+LL_COMMON_API extern const LLUUID IMG_EXPLOSION_4;
+LL_COMMON_API extern const LLUUID IMG_SMOKE_POOF;
-// first clean starts at 3 AM
-const S32 SANDBOX_FIRST_CLEAN_HOUR = 3;
-// clean every <n> hours
-const S32 SANDBOX_CLEAN_FREQ = 12;
+LL_COMMON_API extern const LLUUID IMG_BIG_EXPLOSION_1;
+LL_COMMON_API extern const LLUUID IMG_BIG_EXPLOSION_2;
-const F32 WIND_SCALE_HACK = 2.0f; // hack to make wind speeds more realistic
+LL_COMMON_API extern const LLUUID IMG_BLOOM1;
+LL_COMMON_API extern const LLUUID TERRAIN_DIRT_DETAIL;
+LL_COMMON_API extern const LLUUID TERRAIN_GRASS_DETAIL;
+LL_COMMON_API extern const LLUUID TERRAIN_MOUNTAIN_DETAIL;
+LL_COMMON_API extern const LLUUID TERRAIN_ROCK_DETAIL;
-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
-};
+LL_COMMON_API extern const LLUUID DEFAULT_WATER_NORMAL;
+
+
+// radius within which a chat message is fully audible
+const F32 CHAT_NORMAL_RADIUS = 20.f;
// media commands
const U32 PARCEL_MEDIA_COMMAND_STOP = 0;
@@ -357,53 +225,101 @@ const U32 PARCEL_MEDIA_COMMAND_SIZE = 11;
const U32 PARCEL_MEDIA_COMMAND_DESC = 12;
const U32 PARCEL_MEDIA_COMMAND_LOOP_SET = 13;
-// 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; // No longer supported, 2009-03-02 KLW
-//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;
-const U32 MAP_ITEM_ADULT_EVENT = 0x09;
-const U32 MAP_ITEM_LAND_FOR_SALE_ADULT = 0x0a;
-
-// Region map layer numbers
-const S32 MAP_SIM_OBJECTS = 0;
-const S32 MAP_SIM_TERRAIN = 1;
-const S32 MAP_SIM_LAND_FOR_SALE = 2; // Transparent alpha overlay of land for sale
-const S32 MAP_SIM_IMAGE_TYPES = 3; // Number of map layers
-const S32 MAP_SIM_INFO_MASK = 0x00FFFFFF; // Agent access may be stuffed into upper byte
-const S32 MAP_SIM_LAYER_MASK = 0x0000FFFF; // Layer info is in lower 16 bits
-const S32 MAP_SIM_RETURN_NULL_SIMS = 0x00010000;
-const S32 MAP_SIM_PRELUDE = 0x00020000;
-
-// Crash reporter behavior
-const char* const CRASH_SETTINGS_FILE = "settings_crash_behavior.xml";
-const char* const CRASH_BEHAVIOR_SETTING = "CrashSubmitBehavior";
-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
+// agent constants
+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; // 0x00000001
+const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX; // 0x00000002
+const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX; // 0x00000004
+const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX; // 0x00000008
+const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX; // 0x00000010
+const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX; // 0x00000020
+const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX; // 0x00000040
+const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX; // 0x00000080
+const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX; // 0x00000100
+const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX; // 0x00000200
+
+const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX; // 0x00000400
+const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX; // 0x00000800
+const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX; // 0x00001000
+
+const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX; // 0x00002000
+const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX; // 0x00004000
+const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX; // 0x00008000
+const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX; // 0x00010000
+const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX; // 0x00020000
+const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX; // 0x00040000
+
+const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX; // 0x00080000
+const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX; // 0x00100000
+const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX; // 0x00200000
+const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX; // 0x00400000
+const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX; // 0x00800000
+const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX; // 0x01000000
+const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX; // 0x02000000
+const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX; // 0x04000000
+
+const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX; // 0x08000000
+
+const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX; // 0x10000000
+const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX; // 0x20000000
+const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX; // 0x40000000
+const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX; // 0x80000000
+
+// 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;
+
+// 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)))
+
+// DO NOT CHANGE THE SEQUENCE OF THIS LIST!!
+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;
+const U8 CLICK_ACTION_PLAY = 5;
+const U8 CLICK_ACTION_OPEN_MEDIA = 6;
+const U8 CLICK_ACTION_ZOOM = 7;
+// DO NOT CHANGE THE SEQUENCE OF THIS LIST!!
+
#endif
diff --git a/indra/llcommon/is_approx_equal_fraction.h b/indra/llcommon/is_approx_equal_fraction.h
index d369fbc5b3..4a9b2e2725 100644..100755
--- a/indra/llcommon/is_approx_equal_fraction.h
+++ b/indra/llcommon/is_approx_equal_fraction.h
@@ -6,31 +6,25 @@
* file in llcommon so we can use lltut.h for llcommon tests without
* making llcommon depend on llmath.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
index 771af01279..5cfcdab41c 100644..100755
--- a/indra/llcommon/linden_common.h
+++ b/indra/llcommon/linden_common.h
@@ -2,31 +2,25 @@
* @file linden_common.h
* @brief Includes common headers that are always safe to include
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -58,34 +52,11 @@
#include <ctime>
#include <iosfwd>
-// Work around Microsoft compiler warnings in STL headers
-#ifdef LL_WINDOWS
-#pragma warning (disable : 4702) // unreachable code
-#pragma warning (disable : 4244) // conversion from time_t to S32
-#endif // LL_WINDOWS
-
-// *TODO: Eliminate these, most library .cpp files don't need them.
-// Add them to llviewerprecompiledheaders.h if necessary.
-#include <list>
-#include <map>
-#include <vector>
-#include <string>
-
-#ifdef LL_WINDOWS
-// Reenable warnings we disabled above
-#pragma warning (3 : 4702) // unreachable code, we like level 3, not 4
-// moved msvc warnings to llpreprocessor.h *TODO - delete this comment after merge conflicts are unlikely -brad
-#endif // LL_WINDOWS
-
// Linden only libs in alpha-order other than stdtypes.h
// *NOTE: Please keep includes here to a minimum, see above.
#include "stdtypes.h"
#include "lldefs.h"
#include "llerror.h"
-#include "llextendedstatus.h"
-// Don't do this, adds 15K lines of header code to every library file.
-//#include "llfasttimer.h"
#include "llfile.h"
-#include "llformat.h"
#endif
diff --git a/indra/llcommon/linked_lists.h b/indra/llcommon/linked_lists.h
deleted file mode 100644
index 3d89a056e6..0000000000
--- a/indra/llcommon/linked_lists.h
+++ /dev/null
@@ -1,943 +0,0 @@
-/**
- * @file linked_lists.h
- * @brief LLLinkedList class header amd implementation file.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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/ll_template_cast.h b/indra/llcommon/ll_template_cast.h
deleted file mode 100644
index cff58ce00d..0000000000
--- a/indra/llcommon/ll_template_cast.h
+++ /dev/null
@@ -1,160 +0,0 @@
-/**
- * @file ll_template_cast.h
- * @author Nat Goodspeed
- * @date 2009-11-21
- * @brief Define ll_template_cast function
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-#if ! defined(LL_LL_TEMPLATE_CAST_H)
-#define LL_LL_TEMPLATE_CAST_H
-
-/**
- * Implementation for ll_template_cast() (q.v.).
- *
- * Default implementation: trying to cast two completely unrelated types
- * returns 0. Typically you'd specify T and U as pointer types, but in fact T
- * can be any type that can be initialized with 0.
- */
-template <typename T, typename U>
-struct ll_template_cast_impl
-{
- T operator()(U)
- {
- return 0;
- }
-};
-
-/**
- * ll_template_cast<T>(some_value) is for use in a template function when
- * some_value might be of arbitrary type, but you want to recognize type T
- * specially.
- *
- * It's designed for use with pointer types. Example:
- * @code
- * struct SpecialClass
- * {
- * void someMethod(const std::string&) const;
- * };
- *
- * template <class REALCLASS>
- * void somefunc(const REALCLASS& instance)
- * {
- * const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
- * if (ptr)
- * {
- * ptr->someMethod("Call method only available on SpecialClass");
- * }
- * }
- * @endcode
- *
- * Why is this better than dynamic_cast<>? Because unless OtherClass is
- * polymorphic, the following won't even compile (gcc 4.0.1):
- * @code
- * OtherClass other;
- * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
- * @endcode
- * to say nothing of this:
- * @code
- * void function(int);
- * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
- * @endcode
- * ll_template_cast handles these kinds of cases by returning 0.
- */
-template <typename T, typename U>
-T ll_template_cast(U value)
-{
- return ll_template_cast_impl<T, U>()(value);
-}
-
-/**
- * Implementation for ll_template_cast() (q.v.).
- *
- * Implementation for identical types: return same value.
- */
-template <typename T>
-struct ll_template_cast_impl<T, T>
-{
- T operator()(T value)
- {
- return value;
- }
-};
-
-/**
- * LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
- * type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
- * presuming that @c source can be converted to @c dest by the normal rules of
- * C++.
- *
- * By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
- * type is literally identical to @c dest. (This is because of the
- * straightforward application of template specialization rules.) That can
- * lead to surprising results, e.g.:
- *
- * @code
- * Foo myFoo;
- * const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
- * @endcode
- *
- * Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
- * -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
- * force the compiler to do the right thing.)
- *
- * More disappointingly:
- * @code
- * struct Base {};
- * struct Subclass: public Base {};
- * Subclass object;
- * Base* ptr = ll_template_cast<Base*>(&object);
- * @endcode
- *
- * Here @c ptr will be 0 because <tt>&object</tt> is of type
- * <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
- * succeed, but without our help ll_template_cast can't recognize it.
- *
- * The following would suffice:
- * @code
- * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
- * ...
- * Base* ptr = ll_template_cast<Base*>(&object);
- * @endcode
- *
- * However, as noted earlier, this is easily fooled:
- * @code
- * const Base* ptr = ll_template_cast<const Base*>(&object);
- * @endcode
- * would still produce 0 because we haven't yet seen:
- * @code
- * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
- * @endcode
- *
- * @TODO
- * This macro should use Boost type_traits facilities for stripping and
- * re-adding @c const and @c volatile qualifiers so that invoking
- * LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
- * permitted permutations. It's really not fair to the coder to require
- * separate:
- * @code
- * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
- * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
- * LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
- * @endcode
- *
- * (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
- * because that's not permitted by normal C++ assignment anyway.)
- */
-#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
-template <> \
-struct ll_template_cast_impl<DEST, SOURCE> \
-{ \
- DEST operator()(SOURCE wrapper) \
- { \
- return wrapper; \
- } \
-}
-
-#endif /* ! defined(LL_LL_TEMPLATE_CAST_H) */
diff --git a/indra/llcommon/llagentconstants.h b/indra/llcommon/llagentconstants.h
deleted file mode 100644
index 282db0ae6c..0000000000
--- a/indra/llcommon/llagentconstants.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * @file llagentconstants.h
- * @author James Cook, Andrew Meadows, Richard Nelson
- * @brief Shared constants through the system for agents.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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; // 0x00000001
-const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX; // 0x00000002
-const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX; // 0x00000004
-const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX; // 0x00000008
-const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX; // 0x00000010
-const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX; // 0x00000020
-const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX; // 0x00000040
-const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX; // 0x00000080
-const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX; // 0x00000100
-const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX; // 0x00000200
-
-const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX; // 0x00000400
-const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX; // 0x00000800
-const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX; // 0x00001000
-
-const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX; // 0x00002000
-const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX; // 0x00004000
-const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX; // 0x00008000
-const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX; // 0x00010000
-const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX; // 0x00020000
-const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX; // 0x00040000
-
-const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX; // 0x00080000
-const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX; // 0x00100000
-const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX; // 0x00200000
-const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX; // 0x00400000
-const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX; // 0x00800000
-const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX; // 0x01000000
-const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX; // 0x02000000
-const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX; // 0x04000000
-
-const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX; // 0x08000000
-
-const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX; // 0x10000000
-const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX; // 0x20000000
-const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX; // 0x40000000
-const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX; // 0x80000000
-
-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/llalignedarray.h b/indra/llcommon/llalignedarray.h
new file mode 100644
index 0000000000..b68e9e0f82
--- /dev/null
+++ b/indra/llcommon/llalignedarray.h
@@ -0,0 +1,139 @@
+/**
+ * @file llalignedarray.h
+ * @brief A static array which obeys alignment restrictions and mimics std::vector accessors.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLALIGNEDARRAY_H
+#define LL_LLALIGNEDARRAY_H
+
+#include "llmemory.h"
+
+template <class T, U32 alignment>
+class LLAlignedArray
+{
+public:
+ T* mArray;
+ U32 mElementCount;
+ U32 mCapacity;
+
+ LLAlignedArray();
+ ~LLAlignedArray();
+
+ void push_back(const T& elem);
+ U32 size() const { return mElementCount; }
+ void resize(U32 size);
+ T* append(S32 N);
+ T& operator[](int idx);
+ const T& operator[](int idx) const;
+};
+
+template <class T, U32 alignment>
+LLAlignedArray<T, alignment>::LLAlignedArray()
+{
+ llassert(alignment >= 16);
+ mArray = NULL;
+ mElementCount = 0;
+ mCapacity = 0;
+}
+
+template <class T, U32 alignment>
+LLAlignedArray<T, alignment>::~LLAlignedArray()
+{
+ ll_aligned_free<alignment>(mArray);
+ mArray = NULL;
+ mElementCount = 0;
+ mCapacity = 0;
+}
+
+template <class T, U32 alignment>
+void LLAlignedArray<T, alignment>::push_back(const T& elem)
+{
+ T* old_buf = NULL;
+ if (mCapacity <= mElementCount)
+ {
+ mCapacity++;
+ mCapacity *= 2;
+ T* new_buf = (T*) ll_aligned_malloc<alignment>(mCapacity*sizeof(T));
+ if (mArray)
+ {
+ ll_memcpy_nonaliased_aligned_16((char*)new_buf, (char*)mArray, sizeof(T)*mElementCount);
+ }
+ old_buf = mArray;
+ mArray = new_buf;
+ }
+
+ mArray[mElementCount++] = elem;
+
+ //delete old array here to prevent error on a.push_back(a[0])
+ ll_aligned_free<alignment>(old_buf);
+}
+
+template <class T, U32 alignment>
+void LLAlignedArray<T, alignment>::resize(U32 size)
+{
+ if (mCapacity < size)
+ {
+ mCapacity = size+mCapacity*2;
+ T* new_buf = mCapacity > 0 ? (T*) ll_aligned_malloc<alignment>(mCapacity*sizeof(T)) : NULL;
+ if (mArray)
+ {
+ ll_memcpy_nonaliased_aligned_16((char*) new_buf, (char*) mArray, sizeof(T)*mElementCount);
+ ll_aligned_free<alignment>(mArray);
+ }
+
+ /*for (U32 i = mElementCount; i < mCapacity; ++i)
+ {
+ new(new_buf+i) T();
+ }*/
+ mArray = new_buf;
+ }
+
+ mElementCount = size;
+}
+
+
+template <class T, U32 alignment>
+T& LLAlignedArray<T, alignment>::operator[](int idx)
+{
+ llassert(idx < mElementCount);
+ return mArray[idx];
+}
+
+template <class T, U32 alignment>
+const T& LLAlignedArray<T, alignment>::operator[](int idx) const
+{
+ llassert(idx < mElementCount);
+ return mArray[idx];
+}
+
+template <class T, U32 alignment>
+T* LLAlignedArray<T, alignment>::append(S32 N)
+{
+ U32 sz = size();
+ resize(sz+N);
+ return &((*this)[sz]);
+}
+
+#endif
+
diff --git a/indra/llcommon/llallocator.cpp b/indra/llcommon/llallocator.cpp
index eed9d1e7db..34fc28d8cc 100644..100755
--- a/indra/llcommon/llallocator.cpp
+++ b/indra/llcommon/llallocator.cpp
@@ -2,38 +2,32 @@
* @file llallocator.cpp
* @brief Implementation of the LLAllocator class.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llallocator.h"
-#if LL_USE_TCMALLOC
+#if (LL_USE_TCMALLOC && LL_USE_HEAP_PROFILER)
#include "google/heap-profiler.h"
#include "google/commandlineflags_public.h"
@@ -41,28 +35,6 @@
DECLARE_bool(heap_profile_use_stack_trace);
//DECLARE_double(tcmalloc_release_rate);
-// static
-void LLAllocator::pushMemType(S32 type)
-{
- if(isProfiling())
- {
- PushMemType(type);
- }
-}
-
-// static
-S32 LLAllocator::popMemType()
-{
- if (isProfiling())
- {
- return PopMemType();
- }
- else
- {
- return -1;
- }
-}
-
void LLAllocator::setProfilingEnabled(bool should_enable)
{
// NULL disables dumping to disk
@@ -100,17 +72,6 @@ std::string LLAllocator::getRawProfile()
// stub implementations for when tcmalloc is disabled
//
-// static
-void LLAllocator::pushMemType(S32 type)
-{
-}
-
-// static
-S32 LLAllocator::popMemType()
-{
- return -1;
-}
-
void LLAllocator::setProfilingEnabled(bool should_enable)
{
}
diff --git a/indra/llcommon/llallocator.h b/indra/llcommon/llallocator.h
index 50129b4526..d26ad73c5b 100644..100755
--- a/indra/llcommon/llallocator.h
+++ b/indra/llcommon/llallocator.h
@@ -2,31 +2,25 @@
* @file llallocator.h
* @brief Declaration of the LLAllocator class.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,16 +29,10 @@
#include <string>
-#include "llmemtype.h"
#include "llallocator_heap_profile.h"
class LL_COMMON_API LLAllocator {
friend class LLMemoryView;
- friend class LLMemType;
-
-private:
- static void pushMemType(S32 type);
- static S32 popMemType();
public:
void setProfilingEnabled(bool should_enable);
diff --git a/indra/llcommon/llallocator_heap_profile.cpp b/indra/llcommon/llallocator_heap_profile.cpp
index e50d59fd4b..b2eafde1aa 100644..100755
--- a/indra/llcommon/llallocator_heap_profile.cpp
+++ b/indra/llcommon/llallocator_heap_profile.cpp
@@ -3,31 +3,25 @@
* @brief Implementation of the parser for tcmalloc heap profile data.
* @author Brad Kittenbrink
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -65,7 +59,7 @@ void LLAllocatorHeapProfile::parse(std::string const & prof_text)
{
// *TODO - determine if there should be some better error state than
// mLines being empty. -brad
- llwarns << "invalid heap profile data passed into parser." << llendl;
+ LL_WARNS() << "invalid heap profile data passed into parser." << LL_ENDL;
return;
}
diff --git a/indra/llcommon/llallocator_heap_profile.h b/indra/llcommon/llallocator_heap_profile.h
index 19758df544..69300b829b 100644..100755
--- a/indra/llcommon/llallocator_heap_profile.h
+++ b/indra/llcommon/llallocator_heap_profile.h
@@ -3,31 +3,25 @@
* @brief Declaration of the parser for tcmalloc heap profile data.
* @author Brad Kittenbrink
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index 6b2d1b7c20..5a40845e7d 100644..100755
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -2,37 +2,40 @@
* @file llapp.cpp
* @brief Implementation of the LLApp class.
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
+
#include "llapp.h"
+#include <cstdlib>
+
+#ifdef LL_DARWIN
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#endif
+
#include "llcommon.h"
#include "llapr.h"
#include "llerrorcontrol.h"
@@ -41,7 +44,10 @@
#include "lllivefile.h"
#include "llmemory.h"
#include "llstl.h" // for DeletePointer()
+#include "llstring.h"
#include "lleventtimer.h"
+#include "google_breakpad/exception_handler.h"
+#include "stringize.h"
//
// Signal handling
@@ -49,13 +55,34 @@
// Windows uses structured exceptions, so it's handled a bit differently.
//
#if LL_WINDOWS
+#include "windows.h"
+
LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *exception_infop);
BOOL ConsoleCtrlHandler(DWORD fdwCtrlType);
+bool windows_post_minidump_callback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded);
#else
# include <signal.h>
# include <unistd.h> // for fork()
void setup_signals();
void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+
+#if LL_LINUX
+#include "google_breakpad/minidump_descriptor.h"
+static bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc,
+ void* context,
+ bool succeeded);
+#else
+// Called by breakpad exception handler after the minidump has been generated.
+bool unix_post_minidump_callback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded);
+#endif
+
# if LL_DARWIN
/* OSX doesn't support SIGRT* */
S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
@@ -74,6 +101,10 @@ S32 LL_HEARTBEAT_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-0) : SIGUSR2;
// the static application instance
LLApp* LLApp::sApplication = NULL;
+// Allows the generation of core files for post mortem under gdb
+// and disables crashlogger
+BOOL LLApp::sDisableCrashlogger = FALSE;
+
// Local flag for whether or not to do logging in signal handlers.
//static
BOOL LLApp::sLogInSignal = FALSE;
@@ -81,13 +112,7 @@ BOOL LLApp::sLogInSignal = FALSE;
// static
LLApp::EAppStatus LLApp::sStatus = LLApp::APP_STATUS_STOPPED; // Keeps track of application status
LLAppErrorHandler LLApp::sErrorHandler = NULL;
-LLAppErrorHandler LLApp::sSyncErrorHandler = 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)
@@ -102,11 +127,6 @@ void LLApp::commonCtor()
LLCommon::initClass();
-#if !LL_WINDOWS
- // This must be initialized before the error handler.
- sSigChildCount = new LLAtomicU32(0);
-#endif
-
// 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.
@@ -124,6 +144,12 @@ void LLApp::commonCtor()
// Set the application to this instance.
sApplication = this;
+ mExceptionHandler = 0;
+
+ // initialize the buffer to write the minidump filename to
+ // (this is used to avoid allocating memory in the crash handler)
+ memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH);
+ mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";
}
LLApp::LLApp(LLErrorThread *error_thread) :
@@ -135,10 +161,6 @@ LLApp::LLApp(LLErrorThread *error_thread) :
LLApp::~LLApp()
{
-#if !LL_WINDOWS
- delete sSigChildCount;
- sSigChildCount = NULL;
-#endif
// reclaim live file memory
std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
@@ -152,6 +174,8 @@ LLApp::~LLApp()
delete mThreadErrorp;
mThreadErrorp = NULL;
}
+
+ if(mExceptionHandler != 0) delete mExceptionHandler;
LLCommon::cleanupClass();
}
@@ -185,8 +209,8 @@ bool LLApp::parseCommandOptions(int argc, char** argv)
{
if(argv[ii][0] != '-')
{
- llinfos << "Did not find option identifier while parsing token: "
- << argv[ii] << llendl;
+ LL_INFOS() << "Did not find option identifier while parsing token: "
+ << argv[ii] << LL_ENDL;
return false;
}
int offset = 1;
@@ -211,6 +235,20 @@ bool LLApp::parseCommandOptions(int argc, char** argv)
}
++ii;
value.assign(argv[ii]);
+
+#if LL_WINDOWS
+ //Windows changed command line parsing. Deal with it.
+ S32 slen = value.length() - 1;
+ S32 start = 0;
+ S32 end = slen;
+ if (argv[ii][start]=='"')start++;
+ if (argv[ii][end]=='"')end--;
+ if (start!=0 || end!=slen)
+ {
+ value = value.substr (start,end);
+ }
+#endif
+
commands[name] = value;
}
setOptionData(PRIORITY_COMMAND_LINE, commands);
@@ -255,26 +293,103 @@ void LLApp::stepFrame()
mRunner.run();
}
+#if LL_WINDOWS
+//The following code is needed for 32-bit apps on 64-bit windows to keep it from eating
+//crashes. It is a lovely undocumented 'feature' in SP1 of Windows 7. An excellent
+//in-depth article on the issue may be found here: http://randomascii.wordpress.com/2012/07/05/when-even-crashing-doesn-work/
+void EnableCrashingOnCrashes()
+{
+ typedef BOOL (WINAPI *tGetPolicy)(LPDWORD lpFlags);
+ typedef BOOL (WINAPI *tSetPolicy)(DWORD dwFlags);
+ const DWORD EXCEPTION_SWALLOWING = 0x1;
+
+ HMODULE kernel32 = LoadLibraryA("kernel32.dll");
+ tGetPolicy pGetPolicy = (tGetPolicy)GetProcAddress(kernel32,
+ "GetProcessUserModeExceptionPolicy");
+ tSetPolicy pSetPolicy = (tSetPolicy)GetProcAddress(kernel32,
+ "SetProcessUserModeExceptionPolicy");
+ if (pGetPolicy && pSetPolicy)
+ {
+ DWORD dwFlags;
+ if (pGetPolicy(&dwFlags))
+ {
+ // Turn off the filter
+ pSetPolicy(dwFlags & ~EXCEPTION_SWALLOWING);
+ }
+ }
+}
+#endif
-void LLApp::setupErrorHandling()
+void LLApp::setupErrorHandling(bool second_instance)
{
// 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);
+#if LL_SEND_CRASH_REPORTS
+ EnableCrashingOnCrashes();
// This sets a callback to handle w32 signals to the console window.
// The viewer shouldn't be affected, sicne its a windowed app.
SetConsoleCtrlHandler( (PHANDLER_ROUTINE) ConsoleCtrlHandler, TRUE);
+ // Install the Google Breakpad crash handler for Windows
+ if(mExceptionHandler == 0)
+ {
+ if ( second_instance ) //BUG-5707 Firing teleport from a web browser causes second
+ {
+ mExceptionHandler = new google_breakpad::ExceptionHandler(
+ L"C:\\Temp\\",
+ 0, //No filter
+ windows_post_minidump_callback,
+ 0,
+ google_breakpad::ExceptionHandler::HANDLER_ALL); //No custom client info.
+ }
+ else
+ {
+ LL_WARNS() << "adding breakpad exception handler" << LL_ENDL;
+
+ std::wstring wpipe_name;
+ wpipe_name = mCrashReportPipeStr + wstringize(getPid());
+
+ const std::wstring wdump_path(wstringize(mDumpPath));
+
+ int retries = 30;
+ for (; retries > 0; --retries)
+ {
+ if (mExceptionHandler != 0) delete mExceptionHandler;
+
+ mExceptionHandler = new google_breakpad::ExceptionHandler(
+ wdump_path,
+ NULL, //No filter
+ windows_post_minidump_callback,
+ 0,
+ google_breakpad::ExceptionHandler::HANDLER_ALL,
+ MiniDumpNormal, //Generate a 'normal' minidump.
+ wpipe_name.c_str(),
+ NULL); //No custom client info.
+ if (mExceptionHandler->IsOutOfProcess())
+ {
+ LL_INFOS("CRASHREPORT") << "Successfully attached to Out of Process exception handler." << LL_ENDL;
+ break;
+ }
+ else
+ {
+ LL_WARNS("CRASHREPORT") << "Unable to attach to Out of Process exception handler. " << retries << " retries remaining." << LL_ENDL;
+ ::Sleep(100); //Wait a tick and try again.
+ }
+ }
+
+ if (retries == 0) LL_WARNS("CRASHREPORT") << "Unable to attach to Out of Process exception handler." << LL_ENDL;
+ }
+
+ if (mExceptionHandler)
+ {
+ mExceptionHandler->set_handle_debug_exceptions(true);
+ }
+ }
+#endif
#else
//
// Start up signal handling.
@@ -282,11 +397,61 @@ void LLApp::setupErrorHandling()
// 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();
+
+ // Add google breakpad exception handler configured for Darwin/Linux.
+ bool installHandler = true;
+#if LL_DARWIN
+ // For the special case of Darwin, we do not want to install the handler if
+ // the process is being debugged as the app will exit with value ABRT (6) if
+ // we do. Unfortunately, the code below which performs that test relies on
+ // the structure kinfo_proc which has been tagged by apple as an unstable
+ // API. We disable this test for shipping versions to avoid conflicts with
+ // future releases of Darwin. This test is really only needed for developers
+ // starting the app from a debugger anyway.
+ #ifndef LL_RELEASE_FOR_DOWNLOAD
+ int mib[4];
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROC;
+ mib[2] = KERN_PROC_PID;
+ mib[3] = getpid();
+
+ struct kinfo_proc info;
+ memset(&info, 0, sizeof(info));
+
+ size_t size = sizeof(info);
+ int result = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
+ if((result == 0) || (errno == ENOMEM))
+ {
+ // P_TRACED flag is set, so this process is being debugged; do not install
+ // the handler
+ if(info.kp_proc.p_flag & P_TRACED) installHandler = false;
+ }
+ else
+ {
+ // Failed to discover if the process is being debugged; default to
+ // installing the handler.
+ installHandler = true;
+ }
+ #endif
+ if(installHandler && (mExceptionHandler == 0))
+ {
+ mExceptionHandler = new google_breakpad::ExceptionHandler(mDumpPath, 0, &unix_post_minidump_callback, 0, true, 0);
+ }
+#elif LL_LINUX
+ if(installHandler && (mExceptionHandler == 0))
+ {
+ if (mDumpPath.empty())
+ {
+ mDumpPath = "/tmp";
+ }
+ google_breakpad::MinidumpDescriptor desc(mDumpPath);
+ mExceptionHandler = new google_breakpad::ExceptionHandler(desc, NULL, unix_minidump_callback, NULL, true, -1);
+ }
#endif
+#endif
startErrorThread();
}
@@ -298,7 +463,7 @@ void LLApp::startErrorThread()
//
if(!mThreadErrorp)
{
- llinfos << "Starting error thread" << llendl;
+ LL_INFOS() << "Starting error thread" << LL_ENDL;
mThreadErrorp = new LLErrorThread();
mThreadErrorp->setUserData((void *) this);
mThreadErrorp->start();
@@ -310,21 +475,6 @@ void LLApp::setErrorHandler(LLAppErrorHandler handler)
LLApp::sErrorHandler = handler;
}
-
-void LLApp::setSyncErrorHandler(LLAppErrorHandler handler)
-{
- LLApp::sSyncErrorHandler = handler;
-}
-
-// static
-void LLApp::runSyncErrorHandler()
-{
- if (LLApp::sSyncErrorHandler)
- {
- LLApp::sSyncErrorHandler();
- }
-}
-
// static
void LLApp::runErrorHandler()
{
@@ -333,11 +483,10 @@ void LLApp::runErrorHandler()
LLApp::sErrorHandler();
}
- //llinfos << "App status now STOPPED" << llendl;
+ //LL_INFOS() << "App status now STOPPED" << LL_ENDL;
LLApp::setStopped();
}
-
// static
void LLApp::setStatus(EAppStatus status)
{
@@ -348,15 +497,46 @@ void LLApp::setStatus(EAppStatus status)
// static
void LLApp::setError()
{
- if (!isError())
+ // set app status to ERROR so that the LLErrorThread notices
+ setStatus(APP_STATUS_ERROR);
+}
+
+void LLApp::setMiniDumpDir(const std::string &path)
+{
+ if (path.empty())
+ {
+ mDumpPath = "/tmp";
+ }
+ else
{
- // perform any needed synchronous error-handling
- runSyncErrorHandler();
- // set app status to ERROR so that the LLErrorThread notices
- setStatus(APP_STATUS_ERROR);
+ mDumpPath = path;
}
+
+ if(mExceptionHandler == 0) return;
+#ifdef LL_WINDOWS
+ wchar_t buffer[MAX_MINDUMP_PATH_LENGTH];
+ mbstowcs(buffer, mDumpPath.c_str(), MAX_MINDUMP_PATH_LENGTH);
+ mExceptionHandler->set_dump_path(std::wstring(buffer));
+#elif LL_LINUX
+ //google_breakpad::MinidumpDescriptor desc("/tmp"); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
+ google_breakpad::MinidumpDescriptor desc(mDumpPath); //path works in debug fails in production inside breakpad lib so linux gets a little less stack reporting until it is patched.
+ mExceptionHandler->set_minidump_descriptor(desc);
+#else
+ mExceptionHandler->set_dump_path(mDumpPath);
+#endif
+}
+
+void LLApp::setDebugFileNames(const std::string &path)
+{
+ mStaticDebugFileName = path + "static_debug_info.log";
+ mDynamicDebugFileName = path + "dynamic_debug_info.log";
}
+void LLApp::writeMiniDump()
+{
+ if(mExceptionHandler == 0) return;
+ mExceptionHandler->WriteMinidump();
+}
// static
void LLApp::setQuitting()
@@ -364,7 +544,7 @@ 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;
+ LL_INFOS() << "Setting app state to QUITTING" << LL_ENDL;
setStatus(APP_STATUS_QUITTING);
}
}
@@ -404,39 +584,35 @@ bool LLApp::isQuitting()
return (APP_STATUS_QUITTING == sStatus);
}
+// static
bool LLApp::isExiting()
{
return isQuitting() || isError();
}
-#if !LL_WINDOWS
-// static
-U32 LLApp::getSigChildCount()
+void LLApp::disableCrashlogger()
{
- if (sSigChildCount)
+ // Disable Breakpad exception handler.
+ if (mExceptionHandler != 0)
{
- return U32(*sSigChildCount);
+ delete mExceptionHandler;
+ mExceptionHandler = 0;
}
- return 0;
+
+ sDisableCrashlogger = TRUE;
}
// static
-void LLApp::incSigChildCount()
+bool LLApp::isCrashloggerDisabled()
{
- if (sSigChildCount)
- {
- (*sSigChildCount)++;
- }
+ return (sDisableCrashlogger == TRUE);
}
-#endif
-
-
// static
int LLApp::getPid()
{
#if LL_WINDOWS
- return 0;
+ return GetCurrentProcessId();
#else
return getpid();
#endif
@@ -453,7 +629,7 @@ LONG WINAPI default_windows_exception_handler(struct _EXCEPTION_POINTERS *except
if (LLApp::isError())
{
- llwarns << "Got another fatal signal while in the error handler, die now!" << llendl;
+ LL_WARNS() << "Got another fatal signal while in the error handler, die now!" << LL_ENDL;
retval = EXCEPTION_EXECUTE_HANDLER;
return retval;
}
@@ -499,7 +675,7 @@ BOOL ConsoleCtrlHandler(DWORD fdwCtrlType)
// We're already trying to die, just ignore this signal
if (LLApp::sLogInSignal)
{
- llinfos << "Signal handler - Already trying to quit, ignoring signal!" << llendl;
+ LL_INFOS() << "Signal handler - Already trying to quit, ignoring signal!" << LL_ENDL;
}
return TRUE;
}
@@ -512,43 +688,6 @@ BOOL ConsoleCtrlHandler(DWORD fdwCtrlType)
}
#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()
-{
- fflush(NULL); // flush all buffers before the child inherits them
- 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()
{
@@ -587,6 +726,7 @@ void setup_signals()
// Asynchronous signals that result in core
sigaction(SIGQUIT, &act, NULL);
+
}
void clear_signals()
@@ -636,7 +776,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
if (LLApp::sLogInSignal)
{
- llinfos << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << llendl;
+ LL_INFOS() << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << LL_ENDL;
}
@@ -645,28 +785,15 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
case SIGCHLD:
if (LLApp::sLogInSignal)
{
- llinfos << "Signal handler - Got SIGCHLD from " << info->si_pid << llendl;
+ LL_INFOS() << "Signal handler - Got SIGCHLD from " << info->si_pid << LL_ENDL;
}
- // 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!)
- // TODO: 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;
+ LL_WARNS() << "Signal handler - Got SIGABRT, terminating" << LL_ENDL;
}
clear_signals();
raise(signum);
@@ -676,7 +803,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
case SIGTERM:
if (LLApp::sLogInSignal)
{
- llwarns << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << llendl;
+ LL_WARNS() << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << LL_ENDL;
}
// Graceful exit
// Just set our state to quitting, not error
@@ -685,7 +812,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
// We're already trying to die, just ignore this signal
if (LLApp::sLogInSignal)
{
- llinfos << "Signal handler - Already trying to quit, ignoring signal!" << llendl;
+ LL_INFOS() << "Signal handler - Already trying to quit, ignoring signal!" << LL_ENDL;
}
return;
}
@@ -707,7 +834,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
// Smackdown treated just like any other app termination, for now
if (LLApp::sLogInSignal)
{
- llwarns << "Signal handler - Handling smackdown signal!" << llendl;
+ LL_WARNS() << "Signal handler - Handling smackdown signal!" << LL_ENDL;
}
else
{
@@ -721,7 +848,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
if (LLApp::sLogInSignal)
{
- llwarns << "Signal handler - Handling fatal signal!" << llendl;
+ LL_WARNS() << "Signal handler - Handling fatal signal!" << LL_ENDL;
}
if (LLApp::isError())
{
@@ -731,7 +858,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
if (LLApp::sLogInSignal)
{
- llwarns << "Signal handler - Got another fatal signal while in the error handler, die now!" << llendl;
+ LL_WARNS() << "Signal handler - Got another fatal signal while in the error handler, die now!" << LL_ENDL;
}
raise(signum);
return;
@@ -739,8 +866,17 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
if (LLApp::sLogInSignal)
{
- llwarns << "Signal handler - Flagging error status and waiting for shutdown" << llendl;
+ LL_WARNS() << "Signal handler - Flagging error status and waiting for shutdown" << LL_ENDL;
}
+
+ if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
+ {
+ clear_signals();
+ LL_WARNS() << "Fatal signal received, not handling the crash here, passing back to operating system" << LL_ENDL;
+ raise(signum);
+ return;
+ }
+
// Flag status to ERROR, so thread_error does its work.
LLApp::setError();
// Block in the signal handler until somebody says that we're done.
@@ -751,7 +887,7 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
if (LLApp::sLogInSignal)
{
- llwarns << "Signal handler - App is stopped, reraising signal" << llendl;
+ LL_WARNS() << "Signal handler - App is stopped, reraising signal" << LL_ENDL;
}
clear_signals();
raise(signum);
@@ -759,10 +895,156 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
} else {
if (LLApp::sLogInSignal)
{
- llinfos << "Signal handler - Unhandled signal " << signum << ", ignoring!" << llendl;
+ LL_INFOS() << "Signal handler - Unhandled signal " << signum << ", ignoring!" << LL_ENDL;
}
}
}
}
+#if LL_LINUX
+bool unix_minidump_callback(const google_breakpad::MinidumpDescriptor& minidump_desc, void* context, bool succeeded)
+{
+ // Copy minidump file path into fixed buffer in the app instance to avoid
+ // heap allocations in a crash handler.
+
+ // path format: <dump_dir>/<minidump_id>.dmp
+
+ //HACK: *path points to the buffer in getMiniDumpFilename which has already allocated space
+ //to avoid doing allocation during crash.
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ int dir_path_len = strlen(path);
+
+ // The path must not be truncated.
+ S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH - dir_path_len;
+
+ llassert( (remaining - strlen(minidump_desc.path())) > 5);
+
+ path += dir_path_len;
+
+ if (dir_path_len > 0 && path[-1] != '/')
+ {
+ *path++ = '/';
+ --remaining;
+ }
+
+ strncpy(path, minidump_desc.path(), remaining);
+
+ LL_INFOS() << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL;
+ LLApp::runErrorHandler();
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ clear_signals();
+ return false;
+#else
+ return true;
+#endif
+
+}
+#endif
+
+
+bool unix_post_minidump_callback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded)
+{
+ // Copy minidump file path into fixed buffer in the app instance to avoid
+ // heap allocations in a crash handler.
+
+ // path format: <dump_dir>/<minidump_id>.dmp
+ int dirPathLength = strlen(dump_dir);
+ int idLength = strlen(minidump_id);
+
+ // The path must not be truncated.
+ llassert((dirPathLength + idLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH);
+
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
+ strncpy(path, dump_dir, remaining);
+ remaining -= dirPathLength;
+ path += dirPathLength;
+ if (remaining > 0 && dirPathLength > 0 && path[-1] != '/')
+ {
+ *path++ = '/';
+ --remaining;
+ }
+ if (remaining > 0)
+ {
+ strncpy(path, minidump_id, remaining);
+ remaining -= idLength;
+ path += idLength;
+ strncpy(path, ".dmp", remaining);
+ }
+
+ LL_INFOS() << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL;
+ LLApp::runErrorHandler();
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ clear_signals();
+ return false;
+#else
+ return true;
+#endif
+}
#endif // !WINDOWS
+
+#ifdef LL_WINDOWS
+bool windows_post_minidump_callback(const wchar_t* dump_path,
+ const wchar_t* minidump_id,
+ void* context,
+ EXCEPTION_POINTERS* exinfo,
+ MDRawAssertionInfo* assertion,
+ bool succeeded)
+{
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ S32 remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
+ size_t bytesUsed;
+
+ LL_INFOS("MINIDUMPCALLBACK") << "Dump file was generated." << LL_ENDL;
+ bytesUsed = wcstombs(path, dump_path, static_cast<size_t>(remaining));
+ remaining -= bytesUsed;
+ path += bytesUsed;
+ if(remaining > 0 && bytesUsed > 0 && path[-1] != '\\')
+ {
+ *path++ = '\\';
+ --remaining;
+ }
+ if(remaining > 0)
+ {
+ bytesUsed = wcstombs(path, minidump_id, static_cast<size_t>(remaining));
+ remaining -= bytesUsed;
+ path += bytesUsed;
+ }
+ if(remaining > 0)
+ {
+ strncpy(path, ".dmp", remaining);
+ }
+
+ LL_INFOS() << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL;
+ // *NOTE:Mani - this code is stolen from LLApp, where its never actually used.
+ //OSMessageBox("Attach Debugger Now", "Error", OSMB_OK);
+ // *TODO: Translate the signals/exceptions into cross-platform stuff
+ // Windows implementation
+ LL_INFOS() << "Entering Windows Exception Handler..." << LL_ENDL;
+
+ if (LLApp::isError())
+ {
+ LL_WARNS() << "Got another fatal signal while in the error handler, die now!" << LL_ENDL;
+ }
+
+ // 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);
+ }
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ return false;
+#else
+ return true;
+#endif
+}
+#endif
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index 27a52cdd99..d9933b3d36 100644..100755
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -2,31 +2,25 @@
* @file llapp.h
* @brief Declaration of the LLApp class.
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,19 +30,16 @@
#include <map>
#include "llrun.h"
#include "llsd.h"
-#include "lloptioninterface.h"
-
// Forward declarations
template <typename Type> class LLAtomic32;
typedef LLAtomic32<U32> LLAtomicU32;
class LLErrorThread;
class LLLiveFile;
#if LL_LINUX
-typedef struct siginfo siginfo_t;
+#include <signal.h>
#endif
typedef void (*LLAppErrorHandler)();
-typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
#if !LL_WINDOWS
extern S32 LL_SMACKDOWN_SIGNAL;
@@ -57,16 +48,13 @@ extern S32 LL_HEARTBEAT_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 LL_COMMON_API LLApp : public LLOptionInterface
+namespace google_breakpad {
+ class ExceptionHandler; // See exception_handler.h
+}
+
+class LL_COMMON_API LLApp
{
friend class LLErrorThread;
public:
@@ -115,7 +103,7 @@ public:
* @param name The name of the option.
* @return Returns the option data.
*/
- virtual LLSD getOption(const std::string& name) const;
+ LLSD getOption(const std::string& name) const;
/**
* @brief Parse command line options and insert them into
@@ -191,6 +179,11 @@ public:
//
virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
+ //
+ // Crash logging
+ //
+ void disableCrashlogger(); // Let the OS handle the crashes
+ static bool isCrashloggerDisabled(); // Get the here above set value
//
// Application status
@@ -203,10 +196,6 @@ public:
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();
/** @name Error handling methods */
@@ -224,31 +213,27 @@ public:
* DO NOT call this method if your application has specialized
* error handling code.
*/
- void setupErrorHandling();
+ void setupErrorHandling(bool mSecondInstance=false);
void setErrorHandler(LLAppErrorHandler handler);
- void setSyncErrorHandler(LLAppErrorHandler handler);
+ static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.
//@}
-
-#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
- // *NOTE: 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
+ // the maximum length of the minidump filename returned by getMiniDumpFilename()
+ static const U32 MAX_MINDUMP_PATH_LENGTH = 256;
+
+ // change the directory where Breakpad minidump files are written to
+ void setMiniDumpDir(const std::string &path);
+ void setDebugFileNames(const std::string &path);
+
+ // Return the Google Breakpad minidump filename after a crash.
+ char *getMiniDumpFilename() { return mMinidumpPath; }
+ std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
+ std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
+
+ // Write out a Google Breakpad minidump file.
+ void writeMiniDump();
+
/**
* @brief Get a reference to the application runner
@@ -270,13 +255,10 @@ 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
+ static BOOL sDisableCrashlogger; // Let the OS handle crashes for us.
+ std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting.
-#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
+ std::string mDumpPath; //output path for google breakpad. Dependency workaround.
/**
* @brief This method is called once a frame to do once a frame tasks.
@@ -286,15 +268,17 @@ protected:
private:
void startErrorThread();
- static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.
- static void runSyncErrorHandler(); // run IMMEDIATELY when we get an error, ran in the context of the faulting thread.
+ // Contains the filename of the minidump file after a crash.
+ char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH];
+
+ std::string mStaticDebugFileName;
+ std::string mDynamicDebugFileName;
// *NOTE: 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;
- static LLAppErrorHandler sSyncErrorHandler;
// Default application threads
LLErrorThread* mThreadErrorp; // Waits for app to go to status ERROR, then runs the error callback
@@ -315,6 +299,8 @@ private:
private:
// the static application instance if it was created.
static LLApp* sApplication;
+
+ google_breakpad::ExceptionHandler * mExceptionHandler;
#if !LL_WINDOWS
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp
index ed70b1d9f2..a548c96002 100644..100755
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -4,36 +4,32 @@
* @date 2004-11-28
* @brief Helper functions for using the apache portable runtime library.
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llapr.h"
+#include "apr_dso.h"
+#include "llthreadlocalstorage.h"
apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool.
@@ -42,12 +38,15 @@ apr_thread_mutex_t *gCallStacksLogMutexp = NULL;
const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
+bool gAPRInitialized = false;
+
void ll_init_apr()
{
+ // Initialize APR and create the global pool
+ apr_initialize();
+
if (!gAPRPoolp)
{
- // Initialize APR and create the global pool
- apr_initialize();
apr_pool_create(&gAPRPoolp, NULL);
// Initialize the logging mutex
@@ -59,11 +58,21 @@ void ll_init_apr()
{
LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(FALSE) ;
}
+
+ LLThreadLocalPointerBase::initAllThreadLocalStorage();
+ gAPRInitialized = true;
}
+bool ll_apr_is_initialized()
+{
+ return gAPRInitialized;
+}
+
void ll_cleanup_apr()
{
+ gAPRInitialized = false;
+
LL_INFOS("APR") << "Cleaning up APR" << LL_ENDL;
if (gLogMutexp)
@@ -82,6 +91,9 @@ void ll_cleanup_apr()
apr_thread_mutex_destroy(gCallStacksLogMutexp);
gCallStacksLogMutexp = NULL;
}
+
+ LLThreadLocalPointerBase::destroyAllThreadLocalStorage();
+
if (gAPRPoolp)
{
apr_pool_destroy(gAPRPoolp);
@@ -231,9 +243,7 @@ void LLVolatileAPRPool::clearVolatileAPRPool()
llassert_always(mNumActiveRef > 0) ;
}
- //paranoia check if the pool is jammed.
- //will remove the check before going to release.
- llassert_always(mNumTotalRef < (FULL_VOLATILE_APR_POOL << 2)) ;
+ llassert(mNumTotalRef <= (FULL_VOLATILE_APR_POOL << 2)) ;
}
BOOL LLVolatileAPRPool::isFull()
@@ -285,14 +295,31 @@ bool ll_apr_warn_status(apr_status_t status)
{
if(APR_SUCCESS == status) return false;
char buf[MAX_STRING]; /* Flawfinder: ignore */
- apr_strerror(status, buf, MAX_STRING);
+ apr_strerror(status, buf, sizeof(buf));
LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
return true;
}
+bool ll_apr_warn_status(apr_status_t status, apr_dso_handle_t *handle)
+{
+ bool result = ll_apr_warn_status(status);
+ // Despite observed truncation of actual Mac dylib load errors, increasing
+ // this buffer to more than MAX_STRING doesn't help: it appears that APR
+ // stores the output in a fixed 255-character internal buffer. (*sigh*)
+ char buf[MAX_STRING]; /* Flawfinder: ignore */
+ apr_dso_error(handle, buf, sizeof(buf));
+ LL_WARNS("APR") << "APR: " << buf << LL_ENDL;
+ return result;
+}
+
void ll_apr_assert_status(apr_status_t status)
{
- llassert(ll_apr_warn_status(status) == false);
+ llassert(! ll_apr_warn_status(status));
+}
+
+void ll_apr_assert_status(apr_status_t status, apr_dso_handle_t *handle)
+{
+ llassert(! ll_apr_warn_status(status, handle));
}
//---------------------------------------------------------------------
@@ -417,7 +444,11 @@ apr_pool_t* LLAPRFile::getAPRFilePool(apr_pool_t* pool)
// File I/O
S32 LLAPRFile::read(void *buf, S32 nbytes)
{
- llassert_always(mFile) ;
+ if(!mFile)
+ {
+ LL_WARNS() << "apr mFile is removed by somebody else. Can not read." << LL_ENDL ;
+ return 0;
+ }
apr_size_t sz = nbytes;
apr_status_t s = apr_file_read(mFile, buf, &sz);
@@ -435,7 +466,11 @@ S32 LLAPRFile::read(void *buf, S32 nbytes)
S32 LLAPRFile::write(const void *buf, S32 nbytes)
{
- llassert_always(mFile) ;
+ if(!mFile)
+ {
+ LL_WARNS() << "apr mFile is removed by somebody else. Can not write." << LL_ENDL ;
+ return 0;
+ }
apr_size_t sz = nbytes;
apr_status_t s = apr_file_write(mFile, buf, &sz);
@@ -543,14 +578,13 @@ S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nb
return 0;
}
- S32 off;
- if (offset < 0)
- off = LLAPRFile::seek(file_handle, APR_END, 0);
- else
- off = LLAPRFile::seek(file_handle, APR_SET, offset);
+ llassert(offset >= 0);
+
+ if (offset > 0)
+ offset = LLAPRFile::seek(file_handle, APR_SET, offset);
apr_size_t bytes_read;
- if (off < 0)
+ if (offset < 0)
{
bytes_read = 0;
}
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index b05a222b33..b1b0fc4718 100644..100755
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -4,31 +4,25 @@
* @date 2004-11-28
* @brief Helper functions for using the apache portable runtime library.
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -38,27 +32,35 @@
#if LL_LINUX || LL_SOLARIS
#include <sys/param.h> // Need PATH_MAX in APR headers...
#endif
-#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.
- #define WIN32_LEAN_AND_MEAN
- #include <winsock2.h>
- #include <windows.h>
-#endif
#include <boost/noncopyable.hpp>
-
+#include "llwin32headerslean.h"
#include "apr_thread_proc.h"
#include "apr_thread_mutex.h"
#include "apr_getopt.h"
#include "apr_signal.h"
#include "apr_atomic.h"
+
#include "llstring.h"
extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
extern apr_thread_mutex_t* gCallStacksLogMutexp;
+struct apr_dso_handle_t;
+/**
+ * @brief Function which appropriately logs error or remains quiet on
+ * APR_SUCCESS.
+ * @return Returns <code>true</code> if status is an error condition.
+ */
+bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
+/// There's a whole other APR error-message function if you pass a DSO handle.
+bool LL_COMMON_API ll_apr_warn_status(apr_status_t status, apr_dso_handle_t* handle);
+
+void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
+void LL_COMMON_API ll_apr_assert_status(apr_status_t status, apr_dso_handle_t* handle);
+
+extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
+
/**
* @brief initialize the common apr constructs -- apr itself, the
* global pool, and a mutex.
@@ -70,6 +72,9 @@ void LL_COMMON_API ll_init_apr();
*/
void LL_COMMON_API ll_cleanup_apr();
+bool LL_COMMON_API ll_apr_is_initialized();
+
+
//
//LL apr_pool
//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
@@ -168,26 +173,34 @@ public:
~LLAtomic32<Type>() {};
operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
+
+ Type CurrentValue() const { apr_uint32_t data = apr_atomic_read32(const_cast< volatile apr_uint32_t* >(&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--
+ Type operator --(int) { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise)
+
+ Type operator ++() { return apr_atomic_inc32(&mData); } // Type++
+ Type operator --() { return apr_atomic_dec32(&mData); } // approximately --Type (0 if final is 0, non-zero otherwise)
private:
- apr_uint32_t mData;
+ volatile 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
+// Returns NULL if the file fails to open, sets *sizep to file size if 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_A (APR_CREATE|APR_WRITE|APR_APPEND) // "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_AB (APR_CREATE|APR_WRITE|APR_BINARY|APR_APPEND)
#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"
@@ -200,7 +213,7 @@ typedef LLAtomic32<S32> LLAtomicS32;
// especially do not put some time-costly operations between open() and close().
// otherwise it might lock the APRFilePool.
//there are two different apr_pools the APRFile can use:
-// 1, a temperary pool passed to an APRFile function, which is used within this function and only once.
+// 1, a temporary pool passed to an APRFile function, which is used within this function and only once.
// 2, a global pool.
//
@@ -255,19 +268,9 @@ public:
// Returns bytes read/written, 0 if read/write fails:
static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
- static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+ static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append
//*******************************************************************************************************************************
};
-/**
- * @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_COMMON_API ll_apr_warn_status(apr_status_t status);
-
-void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
-
-extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index 6d5b12d840..5ae2df3994 100644..100755
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -2,31 +2,25 @@
* @file llassettype.cpp
* @brief Implementatino of LLAssetType functionality.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,12 +39,16 @@ struct AssetEntry : public LLDictionaryEntry
AssetEntry(const char *desc_name,
const char *type_name, // 8 character limit!
const char *human_name, // for decoding to human readable form; put any and as many printable characters you want in each one
- bool can_link) // can you create a link to this type?
+ bool can_link, // can you create a link to this type?
+ bool can_fetch, // can you fetch this asset by ID?
+ bool can_know) // can you see this asset's ID?
:
LLDictionaryEntry(desc_name),
mTypeName(type_name),
mHumanName(human_name),
- mCanLink(can_link)
+ mCanLink(can_link),
+ mCanFetch(can_fetch),
+ mCanKnow(can_know)
{
llassert(strlen(mTypeName) <= 8);
}
@@ -58,6 +56,8 @@ struct AssetEntry : public LLDictionaryEntry
const char *mTypeName;
const char *mHumanName;
bool mCanLink;
+ bool mCanFetch;
+ bool mCanKnow;
};
class LLAssetDictionary : public LLSingleton<LLAssetDictionary>,
@@ -69,32 +69,35 @@ public:
LLAssetDictionary::LLAssetDictionary()
{
- // DESCRIPTION TYPE NAME HUMAN NAME CAN LINK?
- // |--------------------|-----------|-------------------|-----------|
- addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", FALSE));
- addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", FALSE));
- addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", FALSE));
- addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", FALSE));
- addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", FALSE));
- addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", TRUE));
- addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", TRUE));
- addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", FALSE));
- addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", TRUE));
- addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", FALSE));
- addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", FALSE));
- addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", FALSE));
- addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", TRUE));
- addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", FALSE));
- addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", FALSE));
- addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", FALSE));
- addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", FALSE));
- addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", TRUE));
- addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", FALSE));
-
- addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "symbolic link", FALSE));
- addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "symbolic folder link", FALSE));
-
- addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE));
+ // DESCRIPTION TYPE NAME HUMAN NAME CAN LINK? CAN FETCH? CAN KNOW?
+ // |--------------------|-----------|-------------------|-----------|-----------|---------|
+ addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", true, false, true));
+ addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", true, true, true));
+ addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", true, false, false));
+ addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", true, true, true));
+ addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", true, false, false));
+ addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", true, true, true));
+ addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", true, false, false));
+ addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", true, false, true));
+ addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", true, false, false));
+ addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", true, false, false));
+ addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", true, false, false));
+ addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", true, false, false));
+ addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", true, true, true));
+ addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", true, false, false));
+ addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", true, false, false));
+ addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", true, false, false));
+ addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", true, true, true));
+ addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", true, true, true));
+ addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", false, false, false));
+
+ addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "sym link", false, false, true));
+ addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true));
+ addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false));
+ addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false));
+ addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false));
+ addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, FALSE, FALSE, FALSE));
+
};
// static
@@ -225,3 +228,27 @@ const std::string &LLAssetType::badLookup()
return sBadLookup;
}
+
+// static
+bool LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanFetch;
+ }
+ return false;
+}
+
+// static
+bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanKnow;
+ }
+ return false;
+}
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index c7bbc2e74a..5a95a58d93 100644..100755
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -2,31 +2,25 @@
* @file llassettype.h
* @brief Declaration of LLAssetType.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,8 +29,6 @@
#include <string>
-#include "stdenums.h" // for EDragAndDropType
-
class LL_COMMON_API LLAssetType
{
public:
@@ -115,7 +107,16 @@ public:
AT_LINK_FOLDER = 25,
// Inventory folder link
- AT_COUNT = 26,
+ AT_WIDGET = 40,
+ // UI Widget: this is *not* an inventory asset type, only a viewer side asset (e.g. button, other ui items...)
+
+ AT_PERSON = 45,
+ // A user uuid which is not an inventory asset type, used in viewer only for adding a person to a chat via drag and drop.
+
+ AT_MESH = 49,
+ // Mesh data in our proprietary SLM format
+
+ AT_COUNT = 50,
// +*********************************************************+
// | TO ADD AN ELEMENT TO THIS ENUM: |
@@ -146,6 +147,9 @@ public:
static bool lookupCanLink(EType asset_type);
static bool lookupIsLinkType(EType asset_type);
+ static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download
+ static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer
+
static const std::string& badLookup(); // error string when a lookup fails
protected:
diff --git a/indra/llcommon/llassoclist.h b/indra/llcommon/llassoclist.h
deleted file mode 100644
index 5aa26ea3ce..0000000000
--- a/indra/llcommon/llassoclist.h
+++ /dev/null
@@ -1,302 +0,0 @@
-/**
- * @file llassoclist.h
- * @brief LLAssocList class header file
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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
deleted file mode 100644
index 6517b7017f..0000000000
--- a/indra/llcommon/llavatarconstants.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * @file llavatarconstants.h
- * @brief some useful short term constants for Indra
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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;
-
-// For Flags in AvatarPropertiesReply
-const U32 AVATAR_ALLOW_PUBLISH = 0x1 << 0; // whether profile is externally visible or not
-const U32 AVATAR_MATURE_PUBLISH = 0x1 << 1; // profile is "mature"
-const U32 AVATAR_IDENTIFIED = 0x1 << 2; // whether avatar has provided payment info
-const U32 AVATAR_TRANSACTED = 0x1 << 3; // whether avatar has actively used payment info
-const U32 AVATAR_ONLINE = 0x1 << 4; // the online status of this avatar, if known.
-const U32 AVATAR_AGEVERIFIED = 0x1 << 5; // whether avatar has been age-verified
-
-static const std::string VISIBILITY_DEFAULT("default");
-static const std::string VISIBILITY_HIDDEN("hidden");
-static const std::string VISIBILITY_VISIBLE("visible");
-static const std::string VISIBILITY_INVISIBLE("invisible");
-
-#endif
-
diff --git a/indra/llcommon/llbase32.cpp b/indra/llcommon/llbase32.cpp
index 5e6ced9ce1..349567c90b 100644..100755
--- a/indra/llcommon/llbase32.cpp
+++ b/indra/llcommon/llbase32.cpp
@@ -7,31 +7,25 @@
* http://ghostwhitecrab.com/bitter/
*
* Some parts of this file are:
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -237,8 +231,8 @@ std::string LLBase32::encode(const U8* input, size_t input_size)
size_t encoded = base32_encode(&output[0], output_size, input, input_size);
- llinfos << "encoded " << encoded << " into buffer of size "
- << output_size << llendl;
+ LL_INFOS() << "encoded " << encoded << " into buffer of size "
+ << output_size << LL_ENDL;
}
return output;
}
diff --git a/indra/llcommon/llbase32.h b/indra/llcommon/llbase32.h
index 0697f7b8e2..eeb96d789d 100644..100755
--- a/indra/llcommon/llbase32.h
+++ b/indra/llcommon/llbase32.h
@@ -3,31 +3,25 @@
* @brief base32 encoding that returns a std::string
* @author James Cook
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llbase64.cpp b/indra/llcommon/llbase64.cpp
index 7020a79a22..4e82cf7f20 100644..100755
--- a/indra/llcommon/llbase64.cpp
+++ b/indra/llcommon/llbase64.cpp
@@ -3,31 +3,25 @@
* @brief Wrapper for apr base64 encoding that returns a std::string
* @author James Cook
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llbase64.h b/indra/llcommon/llbase64.h
index c48fea2478..16d2c217d0 100644..100755
--- a/indra/llcommon/llbase64.h
+++ b/indra/llcommon/llbase64.h
@@ -3,31 +3,25 @@
* @brief Wrapper for apr base64 encoding that returns a std::string
* @author James Cook
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llbitpack.cpp b/indra/llcommon/llbitpack.cpp
new file mode 100755
index 0000000000..622a099945
--- /dev/null
+++ b/indra/llcommon/llbitpack.cpp
@@ -0,0 +1,30 @@
+/**
+ * @file bitpack.cpp
+ * @brief LLBitPack class implementation
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+// implementation is all in the header, this include dep ensures the unit test is rerun if the implementation changes.
+#include "llbitpack.h"
diff --git a/indra/llcommon/bitpack.h b/indra/llcommon/llbitpack.h
index cb9c70c235..f99a354cd4 100644..100755
--- a/indra/llcommon/bitpack.h
+++ b/indra/llcommon/llbitpack.h
@@ -1,32 +1,26 @@
/**
- * @file bitpack.h
+ * @file llbitpack.h
* @brief Convert data to packed bit stream
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -85,7 +79,7 @@ public:
*(mBuffer + mBufferSize++) = mLoad;
if (mBufferSize > mMaxSize)
{
- llerror("mBufferSize exceeding mMaxSize!", 0);
+ LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL;
}
mLoadSize = 0;
mLoad = 0x00;
@@ -128,7 +122,7 @@ public:
*(mBuffer + mBufferSize++) = mLoad;
if (mBufferSize > mMaxSize)
{
- llerror("mBufferSize exceeding mMaxSize!", 0);
+ LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL;
}
mLoadSize = 0;
mLoad = 0x00;
@@ -171,8 +165,8 @@ public:
#ifdef _DEBUG
if (mBufferSize > mMaxSize)
{
- llerrs << "mBufferSize exceeding mMaxSize" << llendl;
- llerrs << mBufferSize << " > " << mMaxSize << llendl;
+ LL_ERRS() << "mBufferSize exceeding mMaxSize" << LL_ENDL;
+ LL_ERRS() << mBufferSize << " > " << mMaxSize << LL_ENDL;
}
#endif
mLoad = *(mBuffer + mBufferSize++);
@@ -196,7 +190,7 @@ public:
*(mBuffer + mBufferSize++) = mLoad;
if (mBufferSize > mMaxSize)
{
- llerror("mBufferSize exceeding mMaxSize!", 0);
+ LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL;
}
mLoadSize = 0;
}
diff --git a/indra/llcommon/llboost.h b/indra/llcommon/llboost.h
index c2bde3a097..57d958a51a 100644..100755
--- a/indra/llcommon/llboost.h
+++ b/indra/llcommon/llboost.h
@@ -2,31 +2,25 @@
* @file llboost.h
* @brief helper object & functions for use with boost
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llchat.h b/indra/llcommon/llchat.h
deleted file mode 100644
index f1b9091298..0000000000
--- a/indra/llcommon/llchat.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/**
- * @file llchat.h
- * @author James Cook
- * @brief Chat constants and data structures.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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,
- CHAT_SOURCE_UNKNOWN = 3
-} 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,
- CHAT_TYPE_REGION = 7,
- CHAT_TYPE_OWNER = 8
-} EChatType;
-
-typedef enum e_chat_audible_level
-{
- CHAT_AUDIBLE_NOT = -1,
- CHAT_AUDIBLE_BARELY = 0,
- CHAT_AUDIBLE_FULLY = 1
-} EChatAudible;
-
-typedef enum e_chat_style
-{
- CHAT_STYLE_NORMAL,
- CHAT_STYLE_IRC,
- CHAT_STYLE_HISTORY
-}EChatStyle;
-
-// A piece of chat
-class LLChat
-{
-public:
- LLChat(const std::string& text = LLStringUtil::null)
- : mText(text),
- mFromName(),
- mFromID(),
- mNotifId(),
- mSourceType(CHAT_SOURCE_AGENT),
- mChatType(CHAT_TYPE_NORMAL),
- mAudible(CHAT_AUDIBLE_FULLY),
- mMuted(FALSE),
- mTime(0.0),
- mTimeStr(),
- mPosAgent(),
- mURL(),
- mChatStyle(CHAT_STYLE_NORMAL),
- mSessionID()
- { }
-
- std::string mText; // UTF-8 line of text
- std::string mFromName; // agent or object name
- LLUUID mFromID; // agent id or object id
- LLUUID mNotifId;
- EChatSourceType mSourceType;
- EChatType mChatType;
- EChatAudible mAudible;
- BOOL mMuted; // pass muted chat to maintain list of chatters
- F64 mTime; // viewer only, seconds from viewer start
- std::string mTimeStr;
- LLVector3 mPosAgent;
- std::string mURL;
- EChatStyle mChatStyle;
- LLUUID mSessionID;
-};
-
-#endif
diff --git a/indra/llcommon/llclickaction.h b/indra/llcommon/llclickaction.h
index d4ffbf8634..2f83113af9 100644
--- a/indra/llcommon/llclickaction.h
+++ b/indra/llcommon/llclickaction.h
@@ -3,45 +3,28 @@
* @author James Cook
* @brief Constants for single-click actions on objects
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLCLICKACTION_H
#define LL_LLCLICKACTION_H
-// DO NOT CHANGE THE SEQUENCE OF THIS LIST!!
-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;
-const U8 CLICK_ACTION_PLAY = 5;
-const U8 CLICK_ACTION_OPEN_MEDIA = 6;
-const U8 CLICK_ACTION_ZOOM = 7;
-// DO NOT CHANGE THE SEQUENCE OF THIS LIST!!
#endif
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index 36a0018995..19642b0982 100644..100755
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -1,31 +1,25 @@
/**
* @file llcommon.cpp
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,10 +29,14 @@
#include "llmemory.h"
#include "llthread.h"
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
//static
BOOL LLCommon::sAprInitialized = FALSE;
+static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL;
+
//static
void LLCommon::initClass()
{
@@ -50,15 +48,20 @@ void LLCommon::initClass()
}
LLTimer::initClass();
LLThreadSafeRefCount::initThreadSafeRefCount();
-// LLWorkerThread::initClass();
-// LLFrameCallbackManager::initClass();
+ assert_main_thread(); // Make sure we record the main thread
+ if (!sMasterThreadRecorder)
+ {
+ sMasterThreadRecorder = new LLTrace::ThreadRecorder();
+ LLTrace::set_master_thread_recorder(sMasterThreadRecorder);
+ }
}
//static
void LLCommon::cleanupClass()
{
-// LLFrameCallbackManager::cleanupClass();
-// LLWorkerThread::cleanupClass();
+ delete sMasterThreadRecorder;
+ sMasterThreadRecorder = NULL;
+ LLTrace::set_master_thread_recorder(NULL);
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
LLTimer::cleanupClass();
if (sAprInitialized)
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h
index 05eef25b21..ca9cad5d05 100644..100755
--- a/indra/llcommon/llcommon.h
+++ b/indra/llcommon/llcommon.h
@@ -1,31 +1,25 @@
/**
* @file llcommon.h
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llcommonutils.cpp b/indra/llcommon/llcommonutils.cpp
new file mode 100755
index 0000000000..d82554c202
--- /dev/null
+++ b/indra/llcommon/llcommonutils.cpp
@@ -0,0 +1,56 @@
+/**
+ * @file llcommonutils.h
+ * @brief Commin utils
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llcommonutils.h"
+
+void LLCommonUtils::computeDifference(
+ const uuid_vec_t& vnew,
+ const uuid_vec_t& vcur,
+ uuid_vec_t& vadded,
+ uuid_vec_t& vremoved)
+{
+ uuid_vec_t vnew_copy(vnew);
+ uuid_vec_t vcur_copy(vcur);
+
+ std::sort(vnew_copy.begin(), vnew_copy.end());
+ std::sort(vcur_copy.begin(), vcur_copy.end());
+
+ size_t maxsize = llmax(vnew_copy.size(), vcur_copy.size());
+ vadded.resize(maxsize);
+ vremoved.resize(maxsize);
+
+ uuid_vec_t::iterator it;
+ // what was removed
+ it = set_difference(vcur_copy.begin(), vcur_copy.end(), vnew_copy.begin(), vnew_copy.end(), vremoved.begin());
+ vremoved.erase(it, vremoved.end());
+
+ // what was added
+ it = set_difference(vnew_copy.begin(), vnew_copy.end(), vcur_copy.begin(), vcur_copy.end(), vadded.begin());
+ vadded.erase(it, vadded.end());
+}
+
+// EOF
diff --git a/indra/llcommon/llcommonutils.h b/indra/llcommon/llcommonutils.h
new file mode 100755
index 0000000000..20ada27830
--- /dev/null
+++ b/indra/llcommon/llcommonutils.h
@@ -0,0 +1,53 @@
+/**
+ * @file llcommonutils.h
+ * @brief Common utils
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLCOMMONUTILS_H
+#define LL_LLCOMMONUTILS_H
+
+#include "lluuid.h"
+
+namespace LLCommonUtils
+{
+ /**
+ * Computes difference between 'vnew' and 'vcur' vectors.
+ * Items present in 'vnew' and missing in 'vcur' are treated as added and are copied into 'vadded'
+ * Items missing in 'vnew' and present in 'vcur' are treated as removed and are copied into 'vremoved'
+ *
+ * @param vnew[in] - incoming IDs
+ * @param vcur[in] - current IDs
+ * @param vadded[out] - difference between incoming and current IDS - added IDs
+ * @param vremoved[out] - difference between incoming and current IDS - removed IDs
+ */
+ LL_COMMON_API void computeDifference(
+ const uuid_vec_t& vnew,
+ const uuid_vec_t& vcur,
+ uuid_vec_t& vadded,
+ uuid_vec_t& vremoved);
+};
+
+#endif //LL_LLCOMMONUTILS_H
+
+// EOF
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 377bfaa247..baaddcaed1 100644..100755
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -4,8 +4,25 @@
* @date 2009-06-03
* @brief Implementation for llcoros.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -22,7 +39,12 @@
#include "llerror.h"
#include "stringize.h"
-LLCoros::LLCoros()
+LLCoros::LLCoros():
+ // MAINT-2724: default coroutine stack size too small on Windows.
+ // Previously we used
+ // boost::context::guarded_stack_allocator::default_stacksize();
+ // empirically this is 64KB on Windows and Linux. Try quadrupling.
+ mStackSize(256*1024)
{
// Register our cleanup() method for "mainloop" ticks
LLEventPumps::instance().obtain("mainloop").listen(
@@ -38,7 +60,7 @@ bool LLCoros::cleanup(const LLSD&)
// since last tick?
if (mi->second->exited())
{
- LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
+ LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
@@ -72,7 +94,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
if (mCoros.find(name) == mCoros.end())
{
- LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
+ LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
return name;
}
}
@@ -98,7 +120,7 @@ std::string LLCoros::getNameByID(const void* self_id) const
// passed to us comes.
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
{
- namespace coro_private = boost::coroutines::detail;
+ namespace coro_private = boost::dcoroutines::detail;
if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
== self_id)
{
@@ -108,6 +130,12 @@ std::string LLCoros::getNameByID(const void* self_id) const
return "";
}
+void LLCoros::setStackSize(S32 stacksize)
+{
+ LL_INFOS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL;
+ mStackSize = stacksize;
+}
+
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 141b0df43c..01ee11da1a 100644..100755
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -4,15 +4,32 @@
* @date 2009-06-02
* @brief Manage running boost::coroutine instances
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLCOROS_H)
#define LL_LLCOROS_H
-#include <boost/coroutine/coroutine.hpp>
+#include <boost/dcoroutine/coroutine.hpp>
#include "llsingleton.h"
#include <boost/ptr_container/ptr_map.hpp>
#include <string>
@@ -61,8 +78,8 @@
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
{
public:
- /// Canonical boost::coroutines::coroutine signature we use
- typedef boost::coroutines::coroutine<void()> coro;
+ /// Canonical boost::dcoroutines::coroutine signature we use
+ typedef boost::dcoroutines::coroutine<void()> coro;
/// Canonical 'self' type
typedef coro::self self;
@@ -108,7 +125,7 @@ public:
template <typename CALLABLE>
std::string launch(const std::string& prefix, const CALLABLE& callable)
{
- return launchImpl(prefix, new coro(callable));
+ return launchImpl(prefix, new coro(callable, mStackSize));
}
/**
@@ -135,6 +152,9 @@ public:
/// getName() by self.get_id()
std::string getNameByID(const void* self_id) const;
+ /// for delayed initialization
+ void setStackSize(S32 stacksize);
+
private:
friend class LLSingleton<LLCoros>;
LLCoros();
@@ -142,6 +162,7 @@ private:
std::string generateDistinctName(const std::string& prefix) const;
bool cleanup(const LLSD&);
+ S32 mStackSize;
typedef boost::ptr_map<std::string, coro> CoroMap;
CoroMap mCoros;
};
diff --git a/indra/llcommon/llcrc.cpp b/indra/llcommon/llcrc.cpp
index 7f183dc9ae..626bb1e564 100644..100755
--- a/indra/llcommon/llcrc.cpp
+++ b/indra/llcommon/llcrc.cpp
@@ -2,31 +2,25 @@
* @file llcrc.cpp
* @brief implementation of the crc class.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -168,7 +162,7 @@ void LLCRC::update(const std::string& filename)
{
if (filename.empty())
{
- llerrs << "No filename specified" << llendl;
+ LL_ERRS() << "No filename specified" << LL_ENDL;
return;
}
@@ -191,7 +185,7 @@ void LLCRC::update(const std::string& filename)
if (nread < (size_t) size)
{
- llwarns << "Short read on " << filename << llendl;
+ LL_WARNS() << "Short read on " << filename << LL_ENDL;
}
update(data, nread);
diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h
index 74369062cc..3f41b28ffa 100644..100755
--- a/indra/llcommon/llcrc.h
+++ b/indra/llcommon/llcrc.h
@@ -2,31 +2,25 @@
* @file llcrc.h
* @brief LLCRC class header file.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -47,7 +41,7 @@
// crc.update(fgetc(fp));
// }
// fclose(fp);
-// llinfos << "File crc: " << crc.getCRC() << llendl;
+// LL_INFOS() << "File crc: " << crc.getCRC() << LL_ENDL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class LL_COMMON_API LLCRC
diff --git a/indra/llcommon/llcriticaldamp.cpp b/indra/llcommon/llcriticaldamp.cpp
index b6f715b0ce..54be855f67 100644..100755
--- a/indra/llcommon/llcriticaldamp.cpp
+++ b/indra/llcommon/llcriticaldamp.cpp
@@ -2,49 +2,63 @@
* @file llcriticaldamp.cpp
* @brief Implementation of the critical damping functionality.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llcriticaldamp.h"
+#include <algorithm>
//-----------------------------------------------------------------------------
// static members
//-----------------------------------------------------------------------------
-LLFrameTimer LLCriticalDamp::sInternalTimer;
-std::map<F32, F32> LLCriticalDamp::sInterpolants;
-F32 LLCriticalDamp::sTimeDelta;
+LLFrameTimer LLSmoothInterpolation::sInternalTimer;
+std::vector<LLSmoothInterpolation::Interpolant> LLSmoothInterpolation::sInterpolants;
+F32 LLSmoothInterpolation::sTimeDelta;
+
+// helper functors
+struct LLSmoothInterpolation::CompareTimeConstants
+{
+ bool operator()(const F32& a, const LLSmoothInterpolation::Interpolant& b) const
+ {
+ return a < b.mTimeScale;
+ }
+
+ bool operator()(const LLSmoothInterpolation::Interpolant& a, const F32& b) const
+ {
+ return a.mTimeScale < b; // bottom of a is higher than bottom of b
+ }
+
+ bool operator()(const LLSmoothInterpolation::Interpolant& a, const LLSmoothInterpolation::Interpolant& b) const
+ {
+ return a.mTimeScale < b.mTimeScale; // bottom of a is higher than bottom of b
+ }
+};
//-----------------------------------------------------------------------------
-// LLCriticalDamp()
+// LLSmoothInterpolation()
//-----------------------------------------------------------------------------
-LLCriticalDamp::LLCriticalDamp()
+LLSmoothInterpolation::LLSmoothInterpolation()
{
sTimeDelta = 0.f;
}
@@ -53,43 +67,55 @@ LLCriticalDamp::LLCriticalDamp()
//-----------------------------------------------------------------------------
// updateInterpolants()
//-----------------------------------------------------------------------------
-void LLCriticalDamp::updateInterpolants()
+void LLSmoothInterpolation::updateInterpolants()
{
sTimeDelta = sInternalTimer.getElapsedTimeAndResetF32();
- F32 time_constant;
-
- for (std::map<F32, F32>::iterator iter = sInterpolants.begin();
- iter != sInterpolants.end(); iter++)
+ for (S32 i = 0; i < sInterpolants.size(); i++)
{
- 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;
+ Interpolant& interp = sInterpolants[i];
+ interp.mInterpolant = calcInterpolant(interp.mTimeScale);
}
}
//-----------------------------------------------------------------------------
// getInterpolant()
//-----------------------------------------------------------------------------
-F32 LLCriticalDamp::getInterpolant(const F32 time_constant, BOOL use_cache)
+F32 LLSmoothInterpolation::getInterpolant(F32SecondsImplicit time_constant, bool use_cache)
{
if (time_constant == 0.f)
{
return 1.f;
}
- if (use_cache && sInterpolants.count(time_constant))
+ if (use_cache)
{
- return sInterpolants[time_constant];
+ interpolant_vec_t::iterator find_it = std::lower_bound(sInterpolants.begin(), sInterpolants.end(), time_constant.value(), CompareTimeConstants());
+ if (find_it != sInterpolants.end() && find_it->mTimeScale == time_constant)
+ {
+ return find_it->mInterpolant;
+ }
+ else
+ {
+ Interpolant interp;
+ interp.mTimeScale = time_constant.value();
+ interp.mInterpolant = calcInterpolant(time_constant.value());
+ sInterpolants.insert(find_it, interp);
+ return interp.mInterpolant;
+ }
}
-
- F32 interpolant = 1.f - pow(2.f, -sTimeDelta / time_constant);
- interpolant = llclamp(interpolant, 0.f, 1.f);
- if (use_cache)
+ else
{
- sInterpolants[time_constant] = interpolant;
+ return calcInterpolant(time_constant.value());
+
}
+}
- return interpolant;
+//-----------------------------------------------------------------------------
+// calcInterpolant()
+//-----------------------------------------------------------------------------
+F32 LLSmoothInterpolation::calcInterpolant(F32 time_constant)
+{
+ return llclamp(1.f - powf(2.f, -sTimeDelta / time_constant), 0.f, 1.f);
}
+
diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h
index 1ea5914b5b..1fb6a1af29 100644..100755
--- a/indra/llcommon/llcriticaldamp.h
+++ b/indra/llcommon/llcriticaldamp.h
@@ -3,57 +3,71 @@
* @brief A lightweight class that calculates critical damping constants once
* per frame.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLCRITICALDAMP_H
#define LL_LLCRITICALDAMP_H
-#include <map>
+#include <vector>
#include "llframetimer.h"
+#include "llunits.h"
-class LL_COMMON_API LLCriticalDamp
+class LL_COMMON_API LLSmoothInterpolation
{
public:
- LLCriticalDamp();
+ LLSmoothInterpolation();
// MANIPULATORS
static void updateInterpolants();
// ACCESSORS
- static F32 getInterpolant(const F32 time_constant, BOOL use_cache = TRUE);
+ static F32 getInterpolant(F32SecondsImplicit time_constant, bool use_cache = true);
-protected:
+ template<typename T>
+ static T lerp(T a, T b, F32SecondsImplicit time_constant, bool use_cache = true)
+ {
+ F32 interpolant = getInterpolant(time_constant, use_cache);
+ return ((a * (1.f - interpolant))
+ + (b * interpolant));
+ }
+
+protected:
+ static F32 calcInterpolant(F32 time_constant);
+
+ struct CompareTimeConstants;
static LLFrameTimer sInternalTimer; // frame timer for calculating deltas
- static std::map<F32, F32> sInterpolants;
+ struct Interpolant
+ {
+ F32 mTimeScale;
+ F32 mInterpolant;
+ };
+ typedef std::vector<Interpolant> interpolant_vec_t;
+ static interpolant_vec_t sInterpolants;
static F32 sTimeDelta;
};
+typedef LLSmoothInterpolation LLCriticalDamp;
+
#endif // LL_LLCRITICALDAMP_H
diff --git a/indra/llcommon/llcursortypes.cpp b/indra/llcommon/llcursortypes.cpp
deleted file mode 100644
index 6751c235f6..0000000000
--- a/indra/llcommon/llcursortypes.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * @file llcursortypes.cpp
- * @brief Cursor types and lookup of types from a string
- *
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llcursortypes.h"
-
-ECursorType getCursorFromString(const std::string& cursor_string)
-{
- static std::map<std::string,U32> cursor_string_table;
- if (cursor_string_table.empty())
- {
- cursor_string_table["UI_CURSOR_ARROW"] = UI_CURSOR_ARROW;
- cursor_string_table["UI_CURSOR_WAIT"] = UI_CURSOR_WAIT;
- cursor_string_table["UI_CURSOR_HAND"] = UI_CURSOR_HAND;
- cursor_string_table["UI_CURSOR_IBEAM"] = UI_CURSOR_IBEAM;
- cursor_string_table["UI_CURSOR_CROSS"] = UI_CURSOR_CROSS;
- cursor_string_table["UI_CURSOR_SIZENWSE"] = UI_CURSOR_SIZENWSE;
- cursor_string_table["UI_CURSOR_SIZENESW"] = UI_CURSOR_SIZENESW;
- cursor_string_table["UI_CURSOR_SIZEWE"] = UI_CURSOR_SIZEWE;
- cursor_string_table["UI_CURSOR_SIZENS"] = UI_CURSOR_SIZENS;
- cursor_string_table["UI_CURSOR_NO"] = UI_CURSOR_NO;
- cursor_string_table["UI_CURSOR_WORKING"] = UI_CURSOR_WORKING;
- cursor_string_table["UI_CURSOR_TOOLGRAB"] = UI_CURSOR_TOOLGRAB;
- cursor_string_table["UI_CURSOR_TOOLLAND"] = UI_CURSOR_TOOLLAND;
- cursor_string_table["UI_CURSOR_TOOLFOCUS"] = UI_CURSOR_TOOLFOCUS;
- cursor_string_table["UI_CURSOR_TOOLCREATE"] = UI_CURSOR_TOOLCREATE;
- cursor_string_table["UI_CURSOR_ARROWDRAG"] = UI_CURSOR_ARROWDRAG;
- cursor_string_table["UI_CURSOR_ARROWCOPY"] = UI_CURSOR_ARROWCOPY;
- cursor_string_table["UI_CURSOR_ARROWDRAGMULTI"] = UI_CURSOR_ARROWDRAGMULTI;
- cursor_string_table["UI_CURSOR_ARROWCOPYMULTI"] = UI_CURSOR_ARROWCOPYMULTI;
- cursor_string_table["UI_CURSOR_NOLOCKED"] = UI_CURSOR_NOLOCKED;
- cursor_string_table["UI_CURSOR_ARROWLOCKED"] = UI_CURSOR_ARROWLOCKED;
- cursor_string_table["UI_CURSOR_GRABLOCKED"] = UI_CURSOR_GRABLOCKED;
- cursor_string_table["UI_CURSOR_TOOLTRANSLATE"] = UI_CURSOR_TOOLTRANSLATE;
- cursor_string_table["UI_CURSOR_TOOLROTATE"] = UI_CURSOR_TOOLROTATE;
- cursor_string_table["UI_CURSOR_TOOLSCALE"] = UI_CURSOR_TOOLSCALE;
- cursor_string_table["UI_CURSOR_TOOLCAMERA"] = UI_CURSOR_TOOLCAMERA;
- cursor_string_table["UI_CURSOR_TOOLPAN"] = UI_CURSOR_TOOLPAN;
- cursor_string_table["UI_CURSOR_TOOLZOOMIN"] = UI_CURSOR_TOOLZOOMIN;
- cursor_string_table["UI_CURSOR_TOOLPICKOBJECT3"] = UI_CURSOR_TOOLPICKOBJECT3;
- cursor_string_table["UI_CURSOR_TOOLPLAY"] = UI_CURSOR_TOOLPLAY;
- cursor_string_table["UI_CURSOR_TOOLPAUSE"] = UI_CURSOR_TOOLPAUSE;
- cursor_string_table["UI_CURSOR_TOOLMEDIAOPEN"] = UI_CURSOR_TOOLMEDIAOPEN;
- cursor_string_table["UI_CURSOR_PIPETTE"] = UI_CURSOR_PIPETTE;
- cursor_string_table["UI_CURSOR_TOOLSIT"] = UI_CURSOR_TOOLSIT;
- cursor_string_table["UI_CURSOR_TOOLBUY"] = UI_CURSOR_TOOLBUY;
- cursor_string_table["UI_CURSOR_TOOLOPEN"] = UI_CURSOR_TOOLOPEN;
- }
-
- std::map<std::string,U32>::const_iterator iter = cursor_string_table.find(cursor_string);
-
- if (iter != cursor_string_table.end())
- {
- return (ECursorType)iter->second;
- }
-
- return UI_CURSOR_ARROW;
-}
-
-
-
diff --git a/indra/llcommon/llcursortypes.h b/indra/llcommon/llcursortypes.h
deleted file mode 100644
index 143c2c64cf..0000000000
--- a/indra/llcommon/llcursortypes.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * @file llcursortypes.h
- * @brief Cursor types
- *
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLCURSORTYPES_H
-#define LL_LLCURSORTYPES_H
-
-// If you add types here, add them in LLCursor::getCursorFromString
-enum ECursorType {
- UI_CURSOR_ARROW,
- UI_CURSOR_WAIT,
- UI_CURSOR_HAND,
- UI_CURSOR_IBEAM,
- UI_CURSOR_CROSS,
- UI_CURSOR_SIZENWSE,
- UI_CURSOR_SIZENESW,
- UI_CURSOR_SIZEWE,
- UI_CURSOR_SIZENS,
- UI_CURSOR_NO,
- UI_CURSOR_WORKING,
- UI_CURSOR_TOOLGRAB,
- UI_CURSOR_TOOLLAND,
- UI_CURSOR_TOOLFOCUS,
- UI_CURSOR_TOOLCREATE,
- UI_CURSOR_ARROWDRAG,
- UI_CURSOR_ARROWCOPY, // drag with copy
- UI_CURSOR_ARROWDRAGMULTI,
- UI_CURSOR_ARROWCOPYMULTI, // drag with copy
- UI_CURSOR_NOLOCKED,
- UI_CURSOR_ARROWLOCKED,
- UI_CURSOR_GRABLOCKED,
- UI_CURSOR_TOOLTRANSLATE,
- UI_CURSOR_TOOLROTATE,
- UI_CURSOR_TOOLSCALE,
- UI_CURSOR_TOOLCAMERA,
- UI_CURSOR_TOOLPAN,
- UI_CURSOR_TOOLZOOMIN,
- UI_CURSOR_TOOLPICKOBJECT3,
- UI_CURSOR_TOOLPLAY,
- UI_CURSOR_TOOLPAUSE,
- UI_CURSOR_TOOLMEDIAOPEN,
- UI_CURSOR_PIPETTE,
- UI_CURSOR_TOOLSIT,
- UI_CURSOR_TOOLBUY,
- UI_CURSOR_TOOLOPEN,
- UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor)
-};
-
-LL_COMMON_API ECursorType getCursorFromString(const std::string& cursor_string);
-
-#endif // LL_LLCURSORTYPES_H
diff --git a/indra/llcommon/lldarray.h b/indra/llcommon/lldarray.h
deleted file mode 100644
index 0e56a11d53..0000000000
--- a/indra/llcommon/lldarray.h
+++ /dev/null
@@ -1,229 +0,0 @@
-/**
- * @file lldarray.h
- * @brief Wrapped std::vector for backward compatibility.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLDARRAY_H
-#define LL_LLDARRAY_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())
- {
- S32 res = iter - this->begin();
- typename std::vector<Type>::iterator last = this->end();
- --last;
- *iter = *last;
- this->pop_back();
- return res;
- }
- 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.size(); }
-
- Type& operator[](const Key& k)
- {
- typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k);
- if (iter == mIndexMap.end())
- {
- U32 n = mVector.size();
- mIndexMap[k] = n;
- mVector.resize(n+1);
- llassert(mVector.size() == mIndexMap.size());
- return mVector[n];
- }
- else
- {
- return mVector[iter->second];
- }
- }
-
- const_iterator find(const Key& k) const
- {
- typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k);
- if(iter == mIndexMap.end())
- {
- return mVector.end();
- }
- else
- {
- return mVector.begin() + iter->second;
- }
- }
-};
-
-#endif
diff --git a/indra/llcommon/lldarrayptr.h b/indra/llcommon/lldarrayptr.h
deleted file mode 100644
index 7fde52bdcd..0000000000
--- a/indra/llcommon/lldarrayptr.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * @file lldarrayptr.h
- * @brief Wrapped std::vector for backward compatibility.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LL_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
index de7f2ead74..7a2a0869f4 100644..100755
--- a/indra/llcommon/lldate.cpp
+++ b/indra/llcommon/lldate.cpp
@@ -4,31 +4,25 @@
* @date 2006-02-05
* @brief Implementation of the date class
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,6 +39,7 @@
#include "lltimer.h"
#include "llstring.h"
+#include "llfasttimer.h"
static const F64 DATE_EPOCH = 0.0;
@@ -54,25 +49,22 @@ static const F64 LL_APR_USEC_PER_SEC = 1000000.0;
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(F64SecondsImplicit seconds_since_epoch) :
+ mSecondsSinceEpoch(seconds_since_epoch.value())
+{}
LLDate::LLDate(const std::string& iso8601_date)
{
if(!fromString(iso8601_date))
{
- llwarns << "date " << iso8601_date << " failed to parse; "
- << "ZEROING IT OUT" << llendl;
+ LL_WARNS() << "date " << iso8601_date << " failed to parse; "
+ << "ZEROING IT OUT" << LL_ENDL;
mSecondsSinceEpoch = DATE_EPOCH;
}
}
@@ -94,11 +86,11 @@ std::string LLDate::asRFC1123() const
return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT"));
}
-LLFastTimer::DeclareTimer FT_DATE_FORMAT("Date Format");
+LLTrace::BlockTimerStatHandle FT_DATE_FORMAT("Date Format");
std::string LLDate::toHTTPDateString (std::string fmt) const
{
- LLFastTimer ft1(FT_DATE_FORMAT);
+ LL_RECORD_BLOCK_TIME(FT_DATE_FORMAT);
time_t locSeconds = (time_t) mSecondsSinceEpoch;
struct tm * gmt = gmtime (&locSeconds);
@@ -107,7 +99,7 @@ std::string LLDate::toHTTPDateString (std::string fmt) const
std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
{
- LLFastTimer ft1(FT_DATE_FORMAT);
+ LL_RECORD_BLOCK_TIME(FT_DATE_FORMAT);
// avoid calling setlocale() unnecessarily - it's expensive.
static std::string prev_locale = "";
@@ -121,7 +113,12 @@ std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt)
// use strftime() as it appears to be faster than std::time_put
char buffer[128];
strftime(buffer, 128, fmt.c_str(), gmt);
- return std::string(buffer);
+ std::string res(buffer);
+#if LL_WINDOWS
+ // Convert from locale-dependant charset to UTF-8 (EXT-8524).
+ res = ll_convert_string_to_utf8_string(res);
+#endif
+ return res;
}
void LLDate::toStream(std::ostream& s) const
@@ -248,8 +245,27 @@ bool LLDate::fromStream(std::istream& s)
s >> fractional;
seconds_since_epoch += fractional;
}
- c = s.get(); // skip the Z
- if (c != 'Z') { return false; }
+
+ c = s.peek(); // check for offset
+ if (c == '+' || c == '-')
+ {
+ S32 offset_sign = (c == '+') ? 1 : -1;
+ S32 offset_hours = 0;
+ S32 offset_minutes = 0;
+ S32 offset_in_seconds = 0;
+
+ s >> offset_hours;
+
+ c = s.get(); // skip the colon a get the minutes if there are any
+ if (c == ':')
+ {
+ s >> offset_minutes;
+ }
+
+ offset_in_seconds = (offset_hours * 60 + offset_sign * offset_minutes) * 60;
+ seconds_since_epoch -= offset_in_seconds;
+ }
+ else if (c != 'Z') { return false; } // skip the Z
mSecondsSinceEpoch = seconds_since_epoch;
return true;
diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h
index f8b2f2f163..be2cd2d051 100644..100755
--- a/indra/llcommon/lldate.h
+++ b/indra/llcommon/lldate.h
@@ -4,31 +4,25 @@
* @date 2006-02-05
* @brief Declaration of a simple date class.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,6 +33,7 @@
#include <string>
#include "stdtypes.h"
+#include "llunits.h"
/**
* @class LLDate
@@ -62,9 +57,9 @@ public:
/**
* @brief Construct a date from a seconds since epoch value.
*
- * @pararm seconds_since_epoch The number of seconds since UTC epoch.
+ * @param seconds_since_epoch The number of seconds since UTC epoch.
*/
- LLDate(F64 seconds_since_epoch);
+ LLDate(F64SecondsImplicit seconds_since_epoch);
/**
* @brief Construct a date from a string representation
diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
new file mode 100644
index 0000000000..830443b956
--- /dev/null
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -0,0 +1,188 @@
+/**
+* @file lldeadmantimer.cpp
+* @brief Simple deadman-switch timer.
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+
+#include "lldeadmantimer.h"
+
+
+// *TODO: Currently, this uses lltimer functions for its time
+// aspects and this leaks into the apis in the U64s/F64s. Would
+// like to perhaps switch this over to TSC register-based timers
+// sometime and drop the overhead some more.
+
+
+// Flag states and their meaning:
+// mActive mDone Meaning
+// false false Nothing running, no result available
+// true false Timer running, no result available
+// false true Timer finished, result can be read once
+// true true Not allowed
+//
+LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu)
+ : mHorizon(time_type(llmax(horizon, F64(0.0)) * get_timer_info().mClockFrequency)),
+ mActive(false), // If true, a timer is running.
+ mDone(false), // If true, timer has completed and can be read (once)
+ mStarted(U64L(0)),
+ mExpires(U64L(0)),
+ mStopped(U64L(0)),
+ mCount(U64L(0)),
+ mIncCPU(inc_cpu),
+ mUStartCPU(LLProcInfo::time_type(U64L(0))),
+ mUEndCPU(LLProcInfo::time_type(U64L(0))),
+ mSStartCPU(LLProcInfo::time_type(U64L(0))),
+ mSEndCPU(LLProcInfo::time_type(U64L(0)))
+{}
+
+
+// static
+LLDeadmanTimer::time_type LLDeadmanTimer::getNow()
+{
+ return LLTimer::getCurrentClockCount();
+}
+
+
+void LLDeadmanTimer::start(time_type now)
+{
+ // *TODO: If active, let's complete an existing timer and save
+ // the result to the side. I think this will be useful later.
+ // For now, wipe out anything in progress, start fresh.
+
+ if (! now)
+ {
+ now = LLTimer::getCurrentClockCount();
+ }
+ mActive = true;
+ mDone = false;
+ mStarted = now;
+ mExpires = now + mHorizon;
+ mStopped = now;
+ mCount = U64L(0);
+ if (mIncCPU)
+ {
+ LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU);
+ }
+}
+
+
+void LLDeadmanTimer::stop(time_type now)
+{
+ if (! mActive)
+ {
+ return;
+ }
+
+ if (! now)
+ {
+ now = getNow();
+ }
+ mStopped = now;
+ mActive = false;
+ mDone = true;
+ if (mIncCPU)
+ {
+ LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
+ }
+}
+
+
+bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
+ U64 & user_cpu, U64 & sys_cpu)
+{
+ const bool status(isExpired(now, started, stopped, count));
+ if (status)
+ {
+ user_cpu = U64(mUEndCPU - mUStartCPU);
+ sys_cpu = U64(mSEndCPU - mSStartCPU);
+ }
+ return status;
+}
+
+
+bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count)
+{
+ if (mActive && ! mDone)
+ {
+ if (! now)
+ {
+ now = getNow();
+ }
+
+ if (now >= mExpires)
+ {
+ // mStopped from ringBell() is the value we want
+ mActive = false;
+ mDone = true;
+ }
+ }
+
+ if (! mDone)
+ {
+ return false;
+ }
+
+ started = mStarted * get_timer_info().mClockFrequencyInv;
+ stopped = mStopped * get_timer_info().mClockFrequencyInv;
+ count = mCount;
+ mDone = false;
+
+ return true;
+}
+
+
+void LLDeadmanTimer::ringBell(time_type now, unsigned int count)
+{
+ if (! mActive)
+ {
+ return;
+ }
+
+ if (! now)
+ {
+ now = getNow();
+ }
+
+ if (now >= mExpires)
+ {
+ // Timer has expired, this event will be dropped
+ mActive = false;
+ mDone = true;
+ }
+ else
+ {
+ // Timer renewed, keep going
+ mStopped = now;
+ mExpires = now + mHorizon;
+ mCount += count;
+ if (mIncCPU)
+ {
+ LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU);
+ }
+ }
+
+ return;
+}
+
diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h
new file mode 100644
index 0000000000..980976e176
--- /dev/null
+++ b/indra/llcommon/lldeadmantimer.h
@@ -0,0 +1,214 @@
+/**
+* @file lldeadmantimer.h
+* @brief Interface to a simple event timer with a deadman's switch
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_DEADMANTIMER_H
+#define LL_DEADMANTIMER_H
+
+
+#include "linden_common.h"
+
+#include "lltimer.h"
+#include "llprocinfo.h"
+
+
+/// @file lldeadmantimer.h
+///
+/// There are interesting user-experienced events in the viewer that
+/// would seem to have well-defined start and stop points but which
+/// actually lack such milestones in the code. Such events (like
+/// time to load meshes after logging in, initial inventory load,
+/// display name fetch) can be defined somewhat after-the-fact by
+/// noticing when we no longer perform operations towards their
+/// completion. This class is intended to help in such applications.
+///
+/// What it implements is a deadman's switch (also known as a
+/// keepalive switch and a doorbell switch). The basic operation is
+/// as follows:
+///
+/// * LLDeadmanTimer is instantiated with a horizon value in seconds,
+/// one for each event of interest.
+/// * When an event starts, @see start() is invoked to begin a
+/// timing operation.
+/// * As operations are performed in service of the event (issuing
+/// HTTP requests, receiving responses), @see ringBell() is invoked
+/// to inform the timer that the operation is still active.
+/// * If the operation is canceled or otherwise terminated, @see
+/// stop() can be called to end the timing operation.
+/// * Concurrent with the ringBell() calls, the program makes
+/// periodic (shorter than the horizon but not too short) calls
+/// to @see isExpired() to see if the event has expired due to
+/// either a stop() call or lack of activity (defined as a ringBell()
+/// call in the previous 'horizon' seconds). If it has expired,
+/// the caller also receives start, stop and count values for the
+/// event which the application can then report in whatever manner
+/// it sees fit.
+/// * The timer becomes passive after an isExpired() call that returns
+/// true. It can then be restarted with a new start() call.
+///
+/// Threading: Instances are not thread-safe. They also use
+/// timing code from lltimer.h which is also unsafe.
+///
+/// Allocation: Not refcounted, may be stack or heap allocated.
+///
+
+class LL_COMMON_API LLDeadmanTimer
+{
+public:
+ /// Public types
+
+ /// Low-level time type chosen for compatibility with
+ /// LLTimer::getCurrentClockCount() which is the basis
+ /// of time operations in this class. This is likely
+ /// to change in a future version in a move to TSC-based
+ /// timing.
+ typedef U64 time_type;
+
+public:
+ /// Construct and initialize an LLDeadmanTimer
+ ///
+ /// @param horizon Time, in seconds, after the last @see ringBell()
+ /// call at which point the timer will consider itself
+ /// expired.
+ ///
+ /// @param inc_cpu If true, gather system and user cpu stats while
+ /// running the timer. This does require more syscalls
+ /// during updates. If false, cpu usage data isn't
+ /// collected and will be zero if queried.
+ LLDeadmanTimer(F64 horizon, bool inc_cpu);
+
+ ~LLDeadmanTimer()
+ {}
+
+private:
+ LLDeadmanTimer(const LLDeadmanTimer &); // Not defined
+ void operator=(const LLDeadmanTimer &); // Not defined
+
+public:
+ /// Get the current time. Zero-basis for this time
+ /// representation is not defined and is different on
+ /// different platforms. Do not attempt to compute
+ /// negative times relative to the first value returned,
+ /// there may not be enough 'front porch' on the range
+ /// to prevent wraparound.
+ ///
+ /// Note: Implementation is expected to change in a
+ /// future release as well.
+ ///
+ static time_type getNow();
+
+ /// Begin timing. If the timer is already active, it is reset
+ /// and timing begins now.
+ ///
+ /// @param now Current time as returned by @see
+ /// LLTimer::getCurrentClockCount(). If zero,
+ /// method will lookup current time.
+ ///
+ void start(time_type now);
+
+ /// End timing. Actively declare the end of the event independent
+ /// of the deadman's switch operation. @see isExpired() will return
+ /// true and appropriate values will be returned.
+ ///
+ /// @param now Current time as returned by @see
+ /// LLTimer::getCurrentClockCount(). If zero,
+ /// method will lookup current time.
+ ///
+ void stop(time_type now);
+
+ /// Declare that something interesting happened. This has two
+ /// effects on an unexpired-timer. 1) The expiration time
+ /// is extended for 'horizon' seconds after the 'now' value.
+ /// 2) An internal counter associated with the event is incremented
+ /// by the @ref count parameter. This count is returned via the
+ /// @see isExpired() method.
+ ///
+ /// @param now Current time as returned by @see
+ /// LLTimer::getCurrentClockCount(). If zero,
+ /// method will lookup current time.
+ ///
+ /// @param count Count of events to be associated with
+ /// this bell ringing.
+ ///
+ void ringBell(time_type now, unsigned int count);
+
+ /// Checks the status of the timer. If the timer has expired,
+ /// also returns various timer-related stats. Unlike ringBell(),
+ /// does not extend the horizon, it only checks for expiration.
+ ///
+ /// @param now Current time as returned by @see
+ /// LLTimer::getCurrentClockCount(). If zero,
+ /// method will lookup current time.
+ ///
+ /// @param started If expired, the starting time of the event is
+ /// returned to the caller via this reference.
+ ///
+ /// @param stopped If expired, the ending time of the event is
+ /// returned to the caller via this reference.
+ /// Ending time will be that provided in the
+ /// stop() method or the last ringBell() call
+ /// leading to expiration, whichever (stop() call
+ /// or notice of expiration) happened first.
+ ///
+ /// @param count If expired, the number of ringBell() calls
+ /// made prior to expiration.
+ ///
+ /// @param user_cpu Amount of CPU spent in user mode by the process
+ /// during the event. Value in microseconds and will
+ /// read zero if not enabled by the constructor.
+ ///
+ /// @param sys_cpu Amount of CPU spent in system mode by the process.
+ ///
+ /// @return true if the timer has expired, false otherwise.
+ /// If true, it also returns the started,
+ /// stopped and count values otherwise these are
+ /// left unchanged.
+ ///
+ bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count,
+ U64 & user_cpu, U64 & sys_cpu);
+
+ /// Identical to the six-arugment form except it does without the
+ /// CPU time return if the caller isn't interested in it.
+ bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count);
+
+protected:
+ time_type mHorizon;
+ bool mActive;
+ bool mDone;
+ time_type mStarted;
+ time_type mExpires;
+ time_type mStopped;
+ time_type mCount;
+
+ const bool mIncCPU; // Include CPU metrics in timer
+ LLProcInfo::time_type mUStartCPU;
+ LLProcInfo::time_type mUEndCPU;
+ LLProcInfo::time_type mSStartCPU;
+ LLProcInfo::time_type mSEndCPU;
+};
+
+
+#endif // LL_DEADMANTIMER_H
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
index f3b5ca361f..5a4b8325f4 100644..100755
--- a/indra/llcommon/lldefs.h
+++ b/indra/llcommon/lldefs.h
@@ -2,31 +2,25 @@
* @file lldefs.h
* @brief Various generic constant definitions.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -242,5 +236,13 @@ inline LLDATATYPE llclampb(const LLDATATYPE& a)
return llmin(llmax(a, (LLDATATYPE)0), (LLDATATYPE)255);
}
+template <class LLDATATYPE>
+inline void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
+{
+ LLDATATYPE tmp = lhs;
+ lhs = rhs;
+ rhs = tmp;
+}
+
#endif // LL_LLDEFS_H
diff --git a/indra/llcommon/lldeleteutils.h b/indra/llcommon/lldeleteutils.h
deleted file mode 100644
index d6a0945e46..0000000000
--- a/indra/llcommon/lldeleteutils.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * @file lldeleteutils.h
- * @brief Utility functions to simplify some common pointer-munging idioms.
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LL_DELETE_UTILS_H
-#define LL_DELETE_UTILS_H
-
-// Simple utility functions to eventually replace the common 2-line
-// idiom scattered throughout the viewer codebase. Note that where
-// possible we would rather be using smart pointers of some sort.
-
-template <class T>
-inline void deleteAndClear(T*& ptr)
-{
- delete ptr;
- ptr = NULL;
-}
-
-template <class T>
-inline void deleteAndClearArray(T*& array_ptr)
-{
- delete[] array_ptr;
- array_ptr = NULL;
-}
-
-#endif
diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp
index ffb5cfbdaa..0e72c175cb 100644..100755
--- a/indra/llcommon/lldependencies.cpp
+++ b/indra/llcommon/lldependencies.cpp
@@ -4,8 +4,25 @@
* @date 2008-09-17
* @brief Implementation for lldependencies.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h
index e6229db834..e0294e271b 100644..100755
--- a/indra/llcommon/lldependencies.h
+++ b/indra/llcommon/lldependencies.h
@@ -5,8 +5,25 @@
* @brief LLDependencies: a generic mechanism for expressing "b must follow a,
* but precede c"
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lldepthstack.h b/indra/llcommon/lldepthstack.h
index accc11f57e..b65840d342 100644..100755
--- a/indra/llcommon/lldepthstack.h
+++ b/indra/llcommon/lldepthstack.h
@@ -2,49 +2,44 @@
* @file lldepthstack.h
* @brief Declaration of the LLDepthStack class
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLDEPTHSTACK_H
#define LL_LLDEPTHSTACK_H
-#include "linked_lists.h"
+#include "llstl.h"
template <class DATA_TYPE> class LLDepthStack
{
private:
- LLLinkedList<DATA_TYPE> mStack;
+ std::deque<DATA_TYPE*> mStack;
U32 mCurrentDepth;
U32 mMaxDepth;
public:
- LLDepthStack() : mCurrentDepth(0), mMaxDepth(0) {}
- ~LLDepthStack() {}
+ LLDepthStack()
+ : mCurrentDepth(0), mMaxDepth(0)
+ {}
void setDepth(U32 depth)
{
@@ -60,24 +55,27 @@ public:
{
if (mCurrentDepth < mMaxDepth)
{
- mStack.addData(data);
+ mStack.push_back(data);
mCurrentDepth++;
}
else
{
// the last item falls off stack and is deleted
- mStack.getLastData();
- mStack.deleteCurrentData();
- mStack.addData(data);
+ if (!mStack.empty())
+ {
+ mStack.pop_front();
+ }
+ mStack.push_back(data);
}
}
DATA_TYPE *pop()
{
- DATA_TYPE *tempp = mStack.getFirstData();
- if (tempp)
+ DATA_TYPE *tempp = NULL;
+ if (!mStack.empty())
{
- mStack.removeCurrentData();
+ tempp = mStack.back();
+ mStack.pop_back();
mCurrentDepth--;
}
return tempp;
@@ -85,20 +83,13 @@ public:
DATA_TYPE *check()
{
- DATA_TYPE *tempp = mStack.getFirstData();
- return tempp;
- }
-
- void deleteAllData()
- {
- mCurrentDepth = 0;
- mStack.deleteAllData();
+ return mStack.empty() ? NULL : mStack.back();
}
-
+
void removeAllNodes()
{
mCurrentDepth = 0;
- mStack.removeAllNodes();
+ mStack.clear();
}
};
diff --git a/indra/llcommon/lldictionary.cpp b/indra/llcommon/lldictionary.cpp
index 8730238d92..e16c35ed6a 100644..100755
--- a/indra/llcommon/lldictionary.cpp
+++ b/indra/llcommon/lldictionary.cpp
@@ -2,30 +2,25 @@
* @file lldictionary.cpp
* @brief Lldictionary class header file
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2007, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lldictionary.h b/indra/llcommon/lldictionary.h
index 4a2bfb9f3f..5800ec5e5d 100644..100755
--- a/indra/llcommon/lldictionary.h
+++ b/indra/llcommon/lldictionary.h
@@ -2,30 +2,25 @@
* @file lldictionary.h
* @brief Lldictionary class header file
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2007, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,6 +30,8 @@
#include <map>
#include <string>
+#include "llerror.h"
+
struct LL_COMMON_API LLDictionaryEntry
{
LLDictionaryEntry(const std::string &name);
@@ -83,14 +80,16 @@ protected:
virtual Index notFound() const
{
// default is to assert
- llassert(false);
+ // don't assert -- makes it impossible to work on mesh-development and viewer-development simultaneously
+ // -- davep 2010.10.29
+ //llassert(false);
return Index(-1);
}
void addEntry(Index index, Entry *entry)
{
if (lookup(index))
{
- llerrs << "Dictionary entry already added (attempted to add duplicate entry)" << llendl;
+ LL_ERRS() << "Dictionary entry already added (attempted to add duplicate entry)" << LL_ENDL;
}
(*this)[index] = entry;
}
diff --git a/indra/llcommon/lldlinked.h b/indra/llcommon/lldlinked.h
deleted file mode 100644
index 8b6853212d..0000000000
--- a/indra/llcommon/lldlinked.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/**
- * @file lldlinked.h
- * @brief Declaration of the LLDLinked class.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LL_LLDLINKED_H
-#define LL_LLDLINKED_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/lldoubledispatch.h b/indra/llcommon/lldoubledispatch.h
index 60678d44fb..8ed295b6f1 100755
--- a/indra/llcommon/lldoubledispatch.h
+++ b/indra/llcommon/lldoubledispatch.h
@@ -4,31 +4,25 @@
* @date 2008-11-11
* @brief function calls virtual on more than one parameter
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lldqueueptr.h b/indra/llcommon/lldqueueptr.h
deleted file mode 100644
index 77df47b1f3..0000000000
--- a/indra/llcommon/lldqueueptr.h
+++ /dev/null
@@ -1,358 +0,0 @@
-/**
- * @file lldqueueptr.h
- * @brief LLDynamicQueuePtr declaration
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LL_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 reallocate(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();
- reallocate(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>::reallocate(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)
- {
- reallocate(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
index 043d33066e..4c08074a9c 100644..100755
--- a/indra/llcommon/llendianswizzle.h
+++ b/indra/llcommon/llendianswizzle.h
@@ -2,31 +2,25 @@
* @file llendianswizzle.h
* @brief Functions for in-place bit swizzling
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llenum.h b/indra/llcommon/llenum.h
deleted file mode 100644
index ac6d46baac..0000000000
--- a/indra/llcommon/llenum.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * @file llenum.h
- * @author Tom Yedwab
- * @brief Utility class for storing enum value <-> string lookup.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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;
- }
- }
-
- 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
index bd334a6654..2100989316 100644..100755
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -3,31 +3,25 @@
* @date December 2006
* @brief error message system
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -53,11 +47,31 @@
#include "lllivefile.h"
#include "llsd.h"
#include "llsdserialize.h"
+#include "llsingleton.h"
#include "llstl.h"
#include "lltimer.h"
namespace {
-#if !LL_WINDOWS
+#if LL_WINDOWS
+ void debugger_print(const std::string& s)
+ {
+ // Be careful when calling OutputDebugString as it throws DBG_PRINTEXCEPTION_C
+ // which works just fine under the windows debugger, but can cause users who
+ // have enabled SEHOP exception chain validation to crash due to interactions
+ // between the Win 32-bit exception handling and boost coroutine fiber stacks. BUG-2707
+ //
+ if (IsDebuggerPresent())
+ {
+ // Need UTF16 for Unicode OutputDebugString
+ //
+ if (s.size())
+ {
+ OutputDebugString(utf8str_to_utf16str(s).c_str());
+ OutputDebugString(TEXT("\n"));
+ }
+ }
+ }
+#else
class RecordToSyslog : public LLError::Recorder
{
public:
@@ -99,11 +113,12 @@ namespace {
public:
RecordToFile(const std::string& filename)
{
- mFile.open(filename, llofstream::out | llofstream::app);
+ mFile.open(filename.c_str(), std::ios_base::out | std::ios_base::app);
if (!mFile)
{
- llinfos << "Error setting log file to " << filename << llendl;
+ LL_INFOS() << "Error setting log file to " << filename << LL_ENDL;
}
+ mWantsTime = true;
}
~RecordToFile()
@@ -111,16 +126,12 @@ namespace {
mFile.close();
}
- bool okay() { return mFile; }
-
- virtual bool wantsTime() { return true; }
+ bool okay() { return mFile.good(); }
virtual void recordMessage(LLError::ELevel level,
const std::string& message)
{
mFile << message << std::endl;
- // mFile.flush();
- // *FIX: should we do this?
}
private:
@@ -131,9 +142,10 @@ namespace {
class RecordToStderr : public LLError::Recorder
{
public:
- RecordToStderr(bool timestamp) : mTimestamp(timestamp), mUseANSI(ANSI_PROBE) { }
-
- virtual bool wantsTime() { return mTimestamp; }
+ RecordToStderr(bool timestamp) : mUseANSI(ANSI_PROBE)
+ {
+ mWantsTime = timestamp;
+ }
virtual void recordMessage(LLError::ELevel level,
const std::string& message)
@@ -164,14 +176,19 @@ namespace {
}
private:
- bool mTimestamp;
- enum ANSIState {ANSI_PROBE, ANSI_YES, ANSI_NO};
- ANSIState mUseANSI;
+ enum ANSIState
+ {
+ ANSI_PROBE,
+ ANSI_YES,
+ ANSI_NO
+ } mUseANSI;
+
void colorANSI(const std::string color)
{
// ANSI color code escape sequence
fprintf(stderr, "\033[%sm", color.c_str() );
};
+
bool checkANSI(void)
{
#if LL_LINUX || LL_DARWIN
@@ -204,13 +221,13 @@ namespace {
class RecordToWinDebug: public LLError::Recorder
{
public:
+ RecordToWinDebug()
+ {}
+
virtual void recordMessage(LLError::ELevel level,
const std::string& message)
{
- llutf16string utf16str =
- wstring_to_utf16str(utf8str_to_wstring(message));
- utf16str += '\n';
- OutputDebugString(utf16str.c_str());
+ debugger_print(message);
}
};
#endif
@@ -222,7 +239,7 @@ namespace
std::string className(const std::type_info& type)
{
#ifdef __GNUC__
- // GCC: type_info::name() returns a mangled class name, must demangle
+ // GCC: type_info::name() returns a mangled class name,st demangle
static size_t abi_name_len = 100;
static char* abi_name_buf = (char*)malloc(abi_name_len);
@@ -318,7 +335,7 @@ namespace
LLSD configuration;
{
- llifstream file(filename());
+ llifstream file(filename().c_str());
if (file.is_open())
{
LLSDSerialize::fromXML(configuration, file);
@@ -326,44 +343,45 @@ namespace
if (configuration.isUndefined())
{
- llwarns << filename() << " missing, ill-formed,"
+ LL_WARNS() << filename() << " missing, ill-formed,"
" or simply undefined; not changing configuration"
- << llendl;
+ << LL_ENDL;
return false;
}
}
LLError::configure(configuration);
- llinfos << "logging reconfigured from " << filename() << llendl;
+ LL_INFOS() << "logging reconfigured from " << filename() << LL_ENDL;
return true;
}
typedef std::map<std::string, LLError::ELevel> LevelMap;
- typedef std::vector<LLError::Recorder*> Recorders;
+ typedef std::vector<LLError::RecorderPtr> Recorders;
typedef std::vector<LLError::CallSite*> CallSiteVector;
- class Globals
+ class Globals : public LLSingleton<Globals>
{
public:
+ Globals();
+
std::ostringstream messageStream;
bool messageStreamInUse;
void addCallSite(LLError::CallSite&);
void invalidateCallSites();
-
- static Globals& get();
- // return the one instance of the globals
private:
CallSiteVector callSites;
-
- Globals()
- : messageStreamInUse(false)
- { }
-
};
+ Globals::Globals()
+ : messageStream(),
+ messageStreamInUse(false),
+ callSites()
+ {
+ }
+
void Globals::addCallSite(LLError::CallSite& site)
{
callSites.push_back(&site);
@@ -380,114 +398,111 @@ namespace
callSites.clear();
}
-
- Globals& Globals::get()
- {
- /* This pattern, of returning a reference to a static function
- variable, is to ensure that this global is constructed before
- it is used, no matter what the global initializeation sequence
- is.
- See C++ FAQ Lite, sections 10.12 through 10.14
- */
- static Globals* globals = new Globals;
- return *globals;
- }
}
namespace LLError
{
- class Settings
+ class SettingsConfig : public LLRefCount
{
+ friend class Settings;
+
public:
- bool printLocation;
+ virtual ~SettingsConfig();
+
+ bool mPrintLocation;
- LLError::ELevel defaultLevel;
+ LLError::ELevel mDefaultLevel;
- LevelMap functionLevelMap;
- LevelMap classLevelMap;
- LevelMap fileLevelMap;
- LevelMap tagLevelMap;
- std::map<std::string, unsigned int> uniqueLogMessages;
+ LevelMap mFunctionLevelMap;
+ LevelMap mClassLevelMap;
+ LevelMap mFileLevelMap;
+ LevelMap mTagLevelMap;
+ std::map<std::string, unsigned int> mUniqueLogMessages;
- LLError::FatalFunction crashFunction;
- LLError::TimeFunction timeFunction;
+ LLError::FatalFunction mCrashFunction;
+ LLError::TimeFunction mTimeFunction;
- Recorders recorders;
- Recorder* fileRecorder;
- Recorder* fixedBufferRecorder;
- std::string fileRecorderFileName;
+ Recorders mRecorders;
+ RecorderPtr mFileRecorder;
+ RecorderPtr mFixedBufferRecorder;
+ std::string mFileRecorderFileName;
- int shouldLogCallCounter;
+ int mShouldLogCallCounter;
- static Settings& get();
+ private:
+ SettingsConfig();
+ };
+
+ typedef LLPointer<SettingsConfig> SettingsConfigPtr;
+
+ class Settings : public LLSingleton<Settings>
+ {
+ public:
+ Settings();
+
+ SettingsConfigPtr getSettingsConfig();
- static void reset();
- static Settings* saveAndReset();
- static void restore(Settings*);
+ void reset();
+ SettingsStoragePtr saveAndReset();
+ void restore(SettingsStoragePtr pSettingsStorage);
private:
- Settings()
- : printLocation(false),
- defaultLevel(LLError::LEVEL_DEBUG),
- crashFunction(),
- timeFunction(NULL),
- fileRecorder(NULL),
- fixedBufferRecorder(NULL),
- shouldLogCallCounter(0)
- { }
-
- ~Settings()
- {
- for_each(recorders.begin(), recorders.end(),
- DeletePointer());
- }
-
- static Settings*& getPtr();
+ SettingsConfigPtr mSettingsConfig;
};
- Settings& Settings::get()
+ SettingsConfig::SettingsConfig()
+ : LLRefCount(),
+ mPrintLocation(false),
+ mDefaultLevel(LLError::LEVEL_DEBUG),
+ mFunctionLevelMap(),
+ mClassLevelMap(),
+ mFileLevelMap(),
+ mTagLevelMap(),
+ mUniqueLogMessages(),
+ mCrashFunction(NULL),
+ mTimeFunction(NULL),
+ mRecorders(),
+ mFileRecorder(),
+ mFixedBufferRecorder(),
+ mFileRecorderFileName(),
+ mShouldLogCallCounter(0)
{
- Settings* p = getPtr();
- if (!p)
- {
- reset();
- p = getPtr();
- }
- return *p;
}
-
- void Settings::reset()
+
+ SettingsConfig::~SettingsConfig()
{
- Globals::get().invalidateCallSites();
-
- Settings*& p = getPtr();
- delete p;
- p = new Settings();
+ mRecorders.clear();
+ }
+
+ Settings::Settings()
+ : LLSingleton<Settings>(),
+ mSettingsConfig(new SettingsConfig())
+ {
+ }
+
+ SettingsConfigPtr Settings::getSettingsConfig()
+ {
+ return mSettingsConfig;
}
- Settings* Settings::saveAndReset()
+ void Settings::reset()
{
- Globals::get().invalidateCallSites();
-
- Settings*& p = getPtr();
- Settings* originalSettings = p;
- p = new Settings();
- return originalSettings;
+ Globals::getInstance()->invalidateCallSites();
+ mSettingsConfig = new SettingsConfig();
}
- void Settings::restore(Settings* originalSettings)
+ SettingsStoragePtr Settings::saveAndReset()
{
- Globals::get().invalidateCallSites();
-
- Settings*& p = getPtr();
- delete p;
- p = originalSettings;
+ SettingsStoragePtr oldSettingsConfig(mSettingsConfig.get());
+ reset();
+ return oldSettingsConfig;
}
- Settings*& Settings::getPtr()
+ void Settings::restore(SettingsStoragePtr pSettingsStorage)
{
- static Settings* currentSettings = NULL;
- return currentSettings;
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr newSettingsConfig(dynamic_cast<SettingsConfig *>(pSettingsStorage.get()));
+ mSettingsConfig = newSettingsConfig;
}
}
@@ -498,18 +513,64 @@ namespace LLError
int line,
const std::type_info& class_info,
const char* function,
- const char* broadTag,
- const char* narrowTag,
- bool printOnce)
- : mLevel(level), mFile(file), mLine(line),
- mClassInfo(class_info), mFunction(function),
- mCached(false), mShouldLog(false),
- mBroadTag(broadTag), mNarrowTag(narrowTag), mPrintOnce(printOnce)
- { }
+ bool printOnce,
+ const char** tags,
+ size_t tag_count)
+ : mLevel(level),
+ mFile(file),
+ mLine(line),
+ mClassInfo(class_info),
+ mFunction(function),
+ mCached(false),
+ mShouldLog(false),
+ mPrintOnce(printOnce),
+ mTags(new const char* [tag_count]),
+ mTagCount(tag_count)
+ {
+ for (int i = 0; i < tag_count; i++)
+ {
+ mTags[i] = tags[i];
+ }
+ switch (mLevel)
+ {
+ case LEVEL_DEBUG: mLevelString = "DEBUG:"; break;
+ case LEVEL_INFO: mLevelString = "INFO:"; break;
+ case LEVEL_WARN: mLevelString = "WARNING:"; break;
+ case LEVEL_ERROR: mLevelString = "ERROR:"; break;
+ default: mLevelString = "XXX:"; break;
+ };
+
+ mLocationString = llformat("%s(%d) :", abbreviateFile(mFile).c_str(), mLine);
+#if LL_WINDOWS
+ // DevStudio: __FUNCTION__ already includes the full class name
+#else
+#if LL_LINUX
+ // gross, but typeid comparison seems to always fail here with gcc4.1
+ if (0 != strcmp(mClassInfo.name(), typeid(NoClassInfo).name()))
+#else
+ if (mClassInfo != typeid(NoClassInfo))
+#endif // LL_LINUX
+ {
+ mFunctionString = className(mClassInfo) + "::";
+ }
+#endif
+ mFunctionString += std::string(mFunction) + ":";
+ for (size_t i = 0; i < mTagCount; i++)
+ {
+ mTagString += std::string("#") + mTags[i] + ((i == mTagCount - 1) ? "" : " ");
+ }
+ }
+
+ CallSite::~CallSite()
+ {
+ delete []mTags;
+ }
void CallSite::invalidate()
- { mCached = false; }
+ {
+ mCached = false;
+ }
}
namespace
@@ -540,21 +601,24 @@ namespace
}
- void commonInit(const std::string& dir)
+ void commonInit(const std::string& dir, bool log_to_stderr = true)
{
- LLError::Settings::reset();
+ LLError::Settings::getInstance()->reset();
LLError::setDefaultLevel(LLError::LEVEL_INFO);
LLError::setFatalFunction(LLError::crashAndLoop);
LLError::setTimeFunction(LLError::utcTime);
- if (shouldLogToStderr())
+ // log_to_stderr is only false in the unit and integration tests to keep builds quieter
+ if (log_to_stderr && shouldLogToStderr())
{
- LLError::addRecorder(new RecordToStderr(stderrLogWantsTime()));
+ LLError::RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime()));
+ LLError::addRecorder(recordToStdErr);
}
#if LL_WINDOWS
- LLError::addRecorder(new RecordToWinDebug);
+ LLError::RecorderPtr recordToWinDebug(new RecordToWinDebug());
+ LLError::addRecorder(recordToWinDebug);
#endif
LogControlFile& e = LogControlFile::fromDirectory(dir);
@@ -582,81 +646,81 @@ namespace LLError
}
commonInit(dir);
#if !LL_WINDOWS
- addRecorder(new RecordToSyslog(identity));
+ LLError::RecorderPtr recordToSyslog(new RecordToSyslog(identity));
+ addRecorder(recordToSyslog);
#endif
}
- void initForApplication(const std::string& dir)
+ void initForApplication(const std::string& dir, bool log_to_stderr)
{
- commonInit(dir);
+ commonInit(dir, log_to_stderr);
}
void setPrintLocation(bool print)
{
- Settings& s = Settings::get();
- s.printLocation = print;
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mPrintLocation = print;
}
void setFatalFunction(const FatalFunction& f)
{
- Settings& s = Settings::get();
- s.crashFunction = f;
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mCrashFunction = f;
}
FatalFunction getFatalFunction()
{
- Settings& s = Settings::get();
- return s.crashFunction;
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ return s->mCrashFunction;
}
void setTimeFunction(TimeFunction f)
{
- Settings& s = Settings::get();
- s.timeFunction = f;
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mTimeFunction = f;
}
void setDefaultLevel(ELevel level)
{
- Globals& g = Globals::get();
- Settings& s = Settings::get();
- g.invalidateCallSites();
- s.defaultLevel = level;
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mDefaultLevel = level;
+ }
+
+ ELevel getDefaultLevel()
+ {
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ return s->mDefaultLevel;
}
void setFunctionLevel(const std::string& function_name, ELevel level)
{
- Globals& g = Globals::get();
- Settings& s = Settings::get();
- g.invalidateCallSites();
- s.functionLevelMap[function_name] = level;
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mFunctionLevelMap[function_name] = level;
}
void setClassLevel(const std::string& class_name, ELevel level)
{
- Globals& g = Globals::get();
- Settings& s = Settings::get();
- g.invalidateCallSites();
- s.classLevelMap[class_name] = level;
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mClassLevelMap[class_name] = level;
}
void setFileLevel(const std::string& file_name, ELevel level)
{
- Globals& g = Globals::get();
- Settings& s = Settings::get();
- g.invalidateCallSites();
- s.fileLevelMap[file_name] = level;
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mFileLevelMap[file_name] = level;
}
void setTagLevel(const std::string& tag_name, ELevel level)
{
- Globals& g = Globals::get();
- Settings& s = Settings::get();
- g.invalidateCallSites();
- s.tagLevelMap[tag_name] = level;
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mTagLevelMap[tag_name] = level;
}
-}
-namespace {
LLError::ELevel decodeLevel(std::string name)
{
static LevelMap level_names;
@@ -675,13 +739,15 @@ namespace {
LevelMap::const_iterator i = level_names.find(name);
if (i == level_names.end())
{
- llwarns << "unrecognized logging level: '" << name << "'" << llendl;
+ LL_WARNS() << "unrecognized logging level: '" << name << "'" << LL_ENDL;
return LLError::LEVEL_INFO;
}
return i->second;
}
-
+}
+
+namespace {
void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
{
LLSD::array_const_iterator i, end;
@@ -696,15 +762,14 @@ namespace LLError
{
void configure(const LLSD& config)
{
- Globals& g = Globals::get();
- Settings& s = Settings::get();
+ Globals::getInstance()->invalidateCallSites();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- g.invalidateCallSites();
- s.functionLevelMap.clear();
- s.classLevelMap.clear();
- s.fileLevelMap.clear();
- s.tagLevelMap.clear();
- s.uniqueLogMessages.clear();
+ s->mFunctionLevelMap.clear();
+ s->mClassLevelMap.clear();
+ s->mFileLevelMap.clear();
+ s->mTagLevelMap.clear();
+ s->mUniqueLogMessages.clear();
setPrintLocation(config["print-location"]);
setDefaultLevel(decodeLevel(config["default-level"]));
@@ -717,10 +782,10 @@ namespace LLError
ELevel level = decodeLevel(entry["level"]);
- setLevels(s.functionLevelMap, entry["functions"], level);
- setLevels(s.classLevelMap, entry["classes"], level);
- setLevels(s.fileLevelMap, entry["files"], level);
- setLevels(s.tagLevelMap, entry["tags"], level);
+ setLevels(s->mFunctionLevelMap, entry["functions"], level);
+ setLevels(s->mClassLevelMap, entry["classes"], level);
+ setLevels(s->mFileLevelMap, entry["files"], level);
+ setLevels(s->mTagLevelMap, entry["tags"], level);
}
}
}
@@ -728,35 +793,67 @@ namespace LLError
namespace LLError
{
+ Recorder::Recorder()
+ : mWantsTime(false),
+ mWantsTags(false),
+ mWantsLevel(true),
+ mWantsLocation(false),
+ mWantsFunctionName(true)
+ {
+ }
+
Recorder::~Recorder()
- { }
+ {
+ }
- // virtual
bool Recorder::wantsTime()
- { return false; }
+ {
+ return mWantsTime;
+ }
+ // virtual
+ bool Recorder::wantsTags()
+ {
+ return mWantsTags;
+ }
+
+ // virtual
+ bool Recorder::wantsLevel()
+ {
+ return mWantsLevel;
+ }
+ // virtual
+ bool Recorder::wantsLocation()
+ {
+ return mWantsLocation;
+ }
- void addRecorder(Recorder* recorder)
+ // virtual
+ bool Recorder::wantsFunctionName()
+ {
+ return mWantsFunctionName;
+ }
+
+ void addRecorder(RecorderPtr recorder)
{
- if (recorder == NULL)
+ if (!recorder)
{
return;
}
- Settings& s = Settings::get();
- s.recorders.push_back(recorder);
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mRecorders.push_back(recorder);
}
- void removeRecorder(Recorder* recorder)
+ void removeRecorder(RecorderPtr recorder)
{
- if (recorder == NULL)
+ if (!recorder)
{
return;
}
- Settings& s = Settings::get();
- s.recorders.erase(
- std::remove(s.recorders.begin(), s.recorders.end(), recorder),
- s.recorders.end());
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder),
+ s->mRecorders.end());
}
}
@@ -764,126 +861,136 @@ namespace LLError
{
void logToFile(const std::string& file_name)
{
- LLError::Settings& s = LLError::Settings::get();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- removeRecorder(s.fileRecorder);
- delete s.fileRecorder;
- s.fileRecorder = NULL;
- s.fileRecorderFileName.clear();
+ removeRecorder(s->mFileRecorder);
+ s->mFileRecorder.reset();
+ s->mFileRecorderFileName.clear();
if (file_name.empty())
{
return;
}
- RecordToFile* f = new RecordToFile(file_name);
- if (!f->okay())
+ RecorderPtr recordToFile(new RecordToFile(file_name));
+ if (boost::dynamic_pointer_cast<RecordToFile>(recordToFile)->okay())
{
- delete f;
- return;
+ s->mFileRecorderFileName = file_name;
+ s->mFileRecorder = recordToFile;
+ addRecorder(recordToFile);
}
-
- s.fileRecorderFileName = file_name;
- s.fileRecorder = f;
- addRecorder(f);
}
void logToFixedBuffer(LLLineBuffer* fixedBuffer)
{
- LLError::Settings& s = LLError::Settings::get();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- removeRecorder(s.fixedBufferRecorder);
- delete s.fixedBufferRecorder;
- s.fixedBufferRecorder = NULL;
+ removeRecorder(s->mFixedBufferRecorder);
+ s->mFixedBufferRecorder.reset();
if (!fixedBuffer)
{
return;
}
- s.fixedBufferRecorder = new RecordToFixedBuffer(fixedBuffer);
- addRecorder(s.fixedBufferRecorder);
+ RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer));
+ s->mFixedBufferRecorder = recordToFixedBuffer;
+ addRecorder(recordToFixedBuffer);
}
std::string logFileName()
{
- LLError::Settings& s = LLError::Settings::get();
- return s.fileRecorderFileName;
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ return s->mFileRecorderFileName;
}
}
namespace
{
- void writeToRecorders(LLError::ELevel level, const std::string& message)
+ void writeToRecorders(const LLError::CallSite& site, const std::string& message, bool show_location = true, bool show_time = true, bool show_tags = true, bool show_level = true, bool show_function = true)
{
- LLError::Settings& s = LLError::Settings::get();
+ LLError::ELevel level = site.mLevel;
+ LLError::SettingsConfigPtr s = LLError::Settings::getInstance()->getSettingsConfig();
- std::string messageWithTime;
-
- for (Recorders::const_iterator i = s.recorders.begin();
- i != s.recorders.end();
+ for (Recorders::const_iterator i = s->mRecorders.begin();
+ i != s->mRecorders.end();
++i)
{
- LLError::Recorder* r = *i;
+ LLError::RecorderPtr r = *i;
- if (r->wantsTime() && s.timeFunction != NULL)
+ std::ostringstream message_stream;
+
+ if (show_location && (r->wantsLocation() || level == LLError::LEVEL_ERROR || s->mPrintLocation))
+ {
+ message_stream << site.mLocationString << " ";
+ }
+
+ if (show_time && r->wantsTime() && s->mTimeFunction != NULL)
{
- if (messageWithTime.empty())
+ message_stream << s->mTimeFunction() << " ";
+ }
+
+ if (show_level && r->wantsLevel())
{
- messageWithTime = s.timeFunction() + " " + message;
+ message_stream << site.mLevelString << " ";
}
- r->recordMessage(level, messageWithTime);
+ if (show_tags && r->wantsTags())
+ {
+ message_stream << site.mTagString << " ";
}
- else
+
+ if (show_function && r->wantsFunctionName())
{
- r->recordMessage(level, message);
+ message_stream << site.mFunctionString << " ";
}
+
+ message_stream << message;
+
+ r->recordMessage(level, message_stream.str());
}
}
}
-
-/*
-Recorder formats:
-
-$type = "ERROR" | "WARNING" | "ALERT" | "INFO" | "DEBUG"
-$loc = "$file($line)"
-$msg = "$loc : " if FATAL or printing loc
- "" otherwise
-$msg += "$type: "
-$msg += contents of stringstream
-
-$time = "%Y-%m-%dT%H:%M:%SZ" if UTC
- or "%Y-%m-%dT%H:%M:%S %Z" if local
-
-syslog: "$msg"
-file: "$time $msg\n"
-stderr: "$time $msg\n" except on windows, "$msg\n"
-fixedbuf: "$msg"
-winddebug: "$msg\n"
-
-Note: if FATAL, an additional line gets logged first, with $msg set to
- "$loc : error"
-
-You get:
- llfoo.cpp(42) : error
- llfoo.cpp(42) : ERROR: something
-
-*/
-
namespace {
bool checkLevelMap(const LevelMap& map, const std::string& key,
LLError::ELevel& level)
{
+ bool stop_checking;
LevelMap::const_iterator i = map.find(key);
if (i == map.end())
{
- return false;
+ return stop_checking = false;
}
level = i->second;
- return true;
+ return stop_checking = true;
+ }
+
+ bool checkLevelMap( const LevelMap& map,
+ const char *const * keys,
+ size_t count,
+ LLError::ELevel& level)
+ {
+ bool found_level = false;
+
+ LLError::ELevel tag_level = LLError::LEVEL_NONE;
+
+ for (size_t i = 0; i < count; i++)
+ {
+ LevelMap::const_iterator it = map.find(keys[i]);
+ if (it != map.end())
+ {
+ found_level = true;
+ tag_level = llmin(tag_level, it->second);
+ }
+ }
+
+ if (found_level)
+ {
+ level = tag_level;
+ }
+ return found_level;
}
class LogLock
@@ -947,32 +1054,37 @@ namespace LLError
return false;
}
- Globals& g = Globals::get();
- Settings& s = Settings::get();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
- s.shouldLogCallCounter += 1;
+ s->mShouldLogCallCounter++;
- std::string class_name = className(site.mClassInfo);
+ const std::string& class_name = className(site.mClassInfo);
std::string function_name = functionName(site.mFunction);
+#if LL_LINUX
+ // gross, but typeid comparison seems to always fail here with gcc4.1
+ if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name()))
+#else
if (site.mClassInfo != typeid(NoClassInfo))
+#endif // LL_LINUX
{
function_name = class_name + "::" + function_name;
}
- ELevel compareLevel = s.defaultLevel;
+ ELevel compareLevel = s->mDefaultLevel;
// The most specific match found will be used as the log level,
// since the computation short circuits.
// So, in increasing order of importance:
- // Default < Broad Tag < File < Class < Function < Narrow Tag
- ((site.mNarrowTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mNarrowTag, compareLevel) : false)
- || checkLevelMap(s.functionLevelMap, function_name, compareLevel)
- || checkLevelMap(s.classLevelMap, class_name, compareLevel)
- || checkLevelMap(s.fileLevelMap, abbreviateFile(site.mFile), compareLevel)
- || ((site.mBroadTag != NULL) ? checkLevelMap(s.tagLevelMap, site.mBroadTag, compareLevel) : false);
+ // Default < Tags < File < Class < Function
+ checkLevelMap(s->mFunctionLevelMap, function_name, compareLevel)
+ || checkLevelMap(s->mClassLevelMap, class_name, compareLevel)
+ || checkLevelMap(s->mFileLevelMap, abbreviateFile(site.mFile), compareLevel)
+ || (site.mTagCount > 0
+ ? checkLevelMap(s->mTagLevelMap, site.mTags, site.mTagCount, compareLevel)
+ : false);
site.mCached = true;
- g.addCallSite(site);
+ Globals::getInstance()->addCallSite(site);
return site.mShouldLog = site.mLevel >= compareLevel;
}
@@ -982,12 +1094,12 @@ namespace LLError
LogLock lock;
if (lock.ok())
{
- Globals& g = Globals::get();
+ Globals* g = Globals::getInstance();
- if (!g.messageStreamInUse)
+ if (!g->messageStreamInUse)
{
- g.messageStreamInUse = true;
- return &g.messageStream;
+ g->messageStreamInUse = true;
+ return &g->messageStream;
}
}
@@ -1012,12 +1124,12 @@ namespace LLError
message[127] = '\0' ;
}
- Globals& g = Globals::get();
- if (out == &g.messageStream)
+ Globals* g = Globals::getInstance();
+ if (out == &g->messageStream)
{
- g.messageStream.clear();
- g.messageStream.str("");
- g.messageStreamInUse = false;
+ g->messageStream.clear();
+ g->messageStream.str("");
+ g->messageStreamInUse = false;
}
else
{
@@ -1034,15 +1146,15 @@ namespace LLError
return;
}
- Globals& g = Globals::get();
- Settings& s = Settings::get();
+ Globals* g = Globals::getInstance();
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
std::string message = out->str();
- if (out == &g.messageStream)
+ if (out == &g->messageStream)
{
- g.messageStream.clear();
- g.messageStream.str("");
- g.messageStreamInUse = false;
+ g->messageStream.clear();
+ g->messageStream.str("");
+ g->messageStreamInUse = false;
}
else
{
@@ -1051,51 +1163,21 @@ namespace LLError
if (site.mLevel == LEVEL_ERROR)
{
- std::ostringstream fatalMessage;
- fatalMessage << abbreviateFile(site.mFile)
- << "(" << site.mLine << ") : error";
-
- writeToRecorders(site.mLevel, fatalMessage.str());
- }
-
-
- std::ostringstream prefix;
-
- switch (site.mLevel)
- {
- case LEVEL_DEBUG: prefix << "DEBUG: "; break;
- case LEVEL_INFO: prefix << "INFO: "; break;
- case LEVEL_WARN: prefix << "WARNING: "; break;
- case LEVEL_ERROR: prefix << "ERROR: "; break;
- default: prefix << "XXX: "; break;
- };
-
- if (s.printLocation)
- {
- prefix << abbreviateFile(site.mFile)
- << "(" << site.mLine << ") : ";
+ writeToRecorders(site, "error", true, true, true, false, false);
}
- #if LL_WINDOWS
- // DevStudio: __FUNCTION__ already includes the full class name
- #else
- if (site.mClassInfo != typeid(NoClassInfo))
- {
- prefix << className(site.mClassInfo) << "::";
- }
- #endif
- prefix << site.mFunction << ": ";
+ std::ostringstream message_stream;
if (site.mPrintOnce)
{
- std::map<std::string, unsigned int>::iterator messageIter = s.uniqueLogMessages.find(message);
- if (messageIter != s.uniqueLogMessages.end())
+ std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message);
+ if (messageIter != s->mUniqueLogMessages.end())
{
messageIter->second++;
unsigned int num_messages = messageIter->second;
if (num_messages == 10 || num_messages == 50 || (num_messages % 100) == 0)
{
- prefix << "ONCE (" << num_messages << "th time seen): ";
+ message_stream << "ONCE (" << num_messages << "th time seen): ";
}
else
{
@@ -1104,36 +1186,32 @@ namespace LLError
}
else
{
- prefix << "ONCE: ";
- s.uniqueLogMessages[message] = 1;
+ message_stream << "ONCE: ";
+ s->mUniqueLogMessages[message] = 1;
}
}
- prefix << message;
- message = prefix.str();
+ message_stream << message;
- writeToRecorders(site.mLevel, message);
+ writeToRecorders(site, message_stream.str());
- if (site.mLevel == LEVEL_ERROR && s.crashFunction)
+ if (site.mLevel == LEVEL_ERROR && s->mCrashFunction)
{
- s.crashFunction(message);
+ s->mCrashFunction(message_stream.str());
}
}
}
-
-
-
namespace LLError
{
- Settings* saveAndResetSettings()
+ SettingsStoragePtr saveAndResetSettings()
{
- return Settings::saveAndReset();
+ return Settings::getInstance()->saveAndReset();
}
- void restoreSettings(Settings* s)
+ void restoreSettings(SettingsStoragePtr pSettingsStorage)
{
- return Settings::restore(s);
+ return Settings::getInstance()->restore(pSettingsStorage);
}
std::string removePrefix(std::string& s, const std::string& p)
@@ -1179,8 +1257,8 @@ namespace LLError
int shouldLogCallCount()
{
- Settings& s = Settings::get();
- return s.shouldLogCallCounter;
+ SettingsConfigPtr s = Settings::getInstance()->getSettingsConfig();
+ return s->mShouldLogCallCounter;
}
#if LL_WINDOWS
@@ -1225,17 +1303,32 @@ namespace LLError
char** LLCallStacks::sBuffer = NULL ;
S32 LLCallStacks::sIndex = 0 ;
+#define SINGLE_THREADED 1
+
class CallStacksLogLock
{
public:
CallStacksLogLock();
~CallStacksLogLock();
+
+#if SINGLE_THREADED
+ bool ok() const { return true; }
+#else
bool ok() const { return mOK; }
private:
bool mLocked;
bool mOK;
+#endif
};
+#if SINGLE_THREADED
+ CallStacksLogLock::CallStacksLogLock()
+ {
+ }
+ CallStacksLogLock::~CallStacksLogLock()
+ {
+ }
+#else
CallStacksLogLock::CallStacksLogLock()
: mLocked(false), mOK(false)
{
@@ -1271,17 +1364,12 @@ namespace LLError
apr_thread_mutex_unlock(gCallStacksLogMutexp);
}
}
+#endif
//static
- void LLCallStacks::push(const char* function, const int line)
+ void LLCallStacks::allocateStackBuffer()
{
- CallStacksLogLock lock;
- if (!lock.ok())
- {
- return;
- }
-
- if(!sBuffer)
+ if(sBuffer == NULL)
{
sBuffer = new char*[512] ;
sBuffer[0] = new char[512 * 128] ;
@@ -1291,6 +1379,31 @@ namespace LLError
}
sIndex = 0 ;
}
+ }
+
+ void LLCallStacks::freeStackBuffer()
+ {
+ if(sBuffer != NULL)
+ {
+ delete [] sBuffer[0] ;
+ delete [] sBuffer ;
+ sBuffer = NULL ;
+ }
+ }
+
+ //static
+ void LLCallStacks::push(const char* function, const int line)
+ {
+ CallStacksLogLock lock;
+ if (!lock.ok())
+ {
+ return;
+ }
+
+ if(sBuffer == NULL)
+ {
+ allocateStackBuffer();
+ }
if(sIndex > 511)
{
@@ -1322,15 +1435,9 @@ namespace LLError
return;
}
- if(!sBuffer)
+ if(sBuffer == NULL)
{
- sBuffer = new char*[512] ;
- sBuffer[0] = new char[512 * 128] ;
- for(S32 i = 1 ; i < 512 ; i++)
- {
- sBuffer[i] = sBuffer[i-1] + 128 ;
- }
- sIndex = 0 ;
+ allocateStackBuffer();
}
if(sIndex > 511)
@@ -1352,20 +1459,18 @@ namespace LLError
if(sIndex > 0)
{
- llinfos << " ************* PRINT OUT LL CALL STACKS ************* " << llendl ;
+ LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL;
while(sIndex > 0)
{
sIndex-- ;
- llinfos << sBuffer[sIndex] << llendl ;
+ LL_INFOS() << sBuffer[sIndex] << LL_ENDL;
}
- llinfos << " *************** END OF LL CALL STACKS *************** " << llendl ;
+ LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL;
}
- if(sBuffer)
+ if(sBuffer != NULL)
{
- delete[] sBuffer[0] ;
- delete[] sBuffer ;
- sBuffer = NULL ;
+ freeStackBuffer();
}
}
@@ -1374,5 +1479,11 @@ namespace LLError
{
sIndex = 0 ;
}
+
+ //static
+ void LLCallStacks::cleanup()
+ {
+ freeStackBuffer();
+ }
}
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 09812de2b8..63040e1772 100644..100755
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -3,31 +3,25 @@
* @date December 2006
* @brief error message system
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,15 +31,73 @@
#include <sstream>
#include <typeinfo>
-#include "llerrorlegacy.h"
#include "stdtypes.h"
+#include "llpreprocessor.h"
+#include <boost/static_assert.hpp>
+
+const int LL_ERR_NOERR = 0;
+
+// 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.
+
+#ifdef _DEBUG
+#define SHOW_DEBUG
+#define SHOW_WARN
+#define SHOW_INFO
+#define SHOW_ASSERT
+#else // _DEBUG
+
+#ifdef LL_RELEASE_WITH_DEBUG_INFO
+#define SHOW_ASSERT
+#endif // LL_RELEASE_WITH_DEBUG_INFO
+
+#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
-/* Error Logging Facility
+#endif // !_DEBUG
+
+#define llassert_always_msg(func, msg) if (LL_UNLIKELY(!(func))) LL_ERRS() << "ASSERT (" << msg << ")" << LL_ENDL
+
+#define llassert_always(func) llassert_always_msg(func, #func)
+
+#ifdef SHOW_ASSERT
+#define llassert(func) llassert_always_msg(func, #func)
+#define llverify(func) llassert_always_msg(func, #func)
+#else
+#define llassert(func)
+#define llverify(func) do {if (func) {}} while(0)
+#endif
+
+#ifdef LL_WINDOWS
+#define LL_STATIC_ASSERT(func, msg) static_assert(func, msg)
+#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(false, msg)
+#else
+#define LL_STATIC_ASSERT(func, msg) BOOST_STATIC_ASSERT(func)
+#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) BOOST_STATIC_ASSERT(sizeof(type) != 0 && false);
+#endif
+
+
+/** Error Logging Facility
Information for most users:
- Code can log messages with constuctions like this:
+ Code can log messages with constructions like this:
LL_INFOS("StringTag") << "request to fizzbip agent " << agent_id
<< " denied due to timeout" << LL_ENDL;
@@ -53,9 +105,9 @@
Messages can be logged to one of four increasing levels of concern,
using one of four "streams":
- LL_DEBUGS("StringTag") - debug messages that are normally supressed
- LL_INFOS("StringTag") - informational messages that are normall shown
- LL_WARNS("StringTag") - warning messages that singal a problem
+ LL_DEBUGS("StringTag") - debug messages that are normally suppressed
+ LL_INFOS("StringTag") - informational messages that are normal shown
+ LL_WARNS("StringTag") - warning messages that signal a problem
LL_ERRS("StringTag") - error messages that are major, unrecoverable failures
The later (LL_ERRS("StringTag")) automatically crashes the process after the message
@@ -96,7 +148,7 @@
WARN: LLFoo::doSomething: called with a big value for i: 283
- Which messages are logged and which are supressed can be controled at run
+ Which messages are logged and which are suppressed can be controlled at run
time from the live file logcontrol.xml based on function, class and/or
source file. See etc/logcontrol-dev.xml for details.
@@ -106,13 +158,12 @@
even release. Which means you can use them to help debug even when deployed
to a real grid.
*/
-
namespace LLError
{
enum ELevel
{
LEVEL_ALL = 0,
- // used to indicate that all messagess should be logged
+ // used to indicate that all messages should be logged
LEVEL_DEBUG = 0,
LEVEL_INFO = 1,
@@ -129,46 +180,62 @@ namespace LLError
They are not intended for general use.
*/
- class CallSite;
+ struct CallSite;
class LL_COMMON_API Log
{
public:
static bool shouldLog(CallSite&);
static std::ostringstream* out();
- static void flush(std::ostringstream* out, char* message) ;
+ static void flush(std::ostringstream* out, char* message);
static void flush(std::ostringstream*, const CallSite&);
};
- class LL_COMMON_API CallSite
+ struct LL_COMMON_API CallSite
{
// Represents a specific place in the code where a message is logged
// This is public because it is used by the macros below. It is not
// intended for public use.
- public:
- CallSite(ELevel, const char* file, int line,
- const std::type_info& class_info, const char* function, const char* broadTag, const char* narrowTag, bool printOnce);
-
+ CallSite(ELevel level,
+ const char* file,
+ int line,
+ const std::type_info& class_info,
+ const char* function,
+ bool print_once,
+ const char** tags,
+ size_t tag_count);
+
+ ~CallSite();
+
+#ifdef LL_LIBRARY_INCLUDE
+ bool shouldLog();
+#else // LL_LIBRARY_INCLUDE
bool shouldLog()
- { return mCached ? mShouldLog : Log::shouldLog(*this); }
+ {
+ return mCached
+ ? mShouldLog
+ : Log::shouldLog(*this);
+ }
// this member function needs to be in-line for efficiency
+#endif // LL_LIBRARY_INCLUDE
void invalidate();
- private:
// these describe the call site and never change
const ELevel mLevel;
const char* const mFile;
- const int mLine;
- const std::type_info& mClassInfo;
+ const int mLine;
+ const std::type_info& mClassInfo;
const char* const mFunction;
- const char* const mBroadTag;
- const char* const mNarrowTag;
- const bool mPrintOnce;
-
- // these implement a cache of the call to shouldLog()
- bool mCached;
- bool mShouldLog;
+ const char** mTags;
+ size_t mTagCount;
+ const bool mPrintOnce;
+ const char* mLevelString;
+ std::string mLocationString,
+ mFunctionString,
+ mTagString;
+ bool mCached,
+ mShouldLog;
friend class Log;
};
@@ -179,7 +246,7 @@ namespace LLError
{ return s; }
// used to indicate the end of a message
- class NoClassInfo { };
+ class LL_COMMON_API NoClassInfo { };
// used to indicate no class info known for logging
//LLCallStacks keeps track of call stacks and output the call stacks to log file
@@ -194,6 +261,9 @@ namespace LLError
private:
static char** sBuffer ;
static S32 sIndex ;
+
+ static void allocateStackBuffer();
+ static void freeStackBuffer();
public:
static void push(const char* function, const int line) ;
@@ -201,21 +271,25 @@ namespace LLError
static void print() ;
static void clear() ;
static void end(std::ostringstream* _out) ;
+ static void cleanup();
};
}
//this is cheaper than llcallstacks if no need to output other variables to call stacks.
-#define llpushcallstacks LLError::LLCallStacks::push(__FUNCTION__, __LINE__)
-#define llcallstacks \
- {\
+#define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__)
+
+#define llcallstacks \
+ { \
std::ostringstream* _out = LLError::LLCallStacks::insert(__FUNCTION__, __LINE__) ; \
(*_out)
-#define llcallstacksendl \
- LLError::End(); \
+
+#define llcallstacksendl \
+ LLError::End(); \
LLError::LLCallStacks::end(_out) ; \
}
-#define llclearcallstacks LLError::LLCallStacks::clear()
-#define llprintcallstacks LLError::LLCallStacks::print()
+
+#define LL_CLEAR_CALLSTACKS() LLError::LLCallStacks::clear()
+#define LL_PRINT_CALLSTACKS() LLError::LLCallStacks::print()
/*
Class type information for logging
@@ -226,81 +300,75 @@ namespace LLError
// See top of file for example of how to use this
typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
- // Outside a class declartion, or in class without LOG_CLASS(), this
+ // Outside a class declaration, or in class without LOG_CLASS(), this
// typedef causes the messages to not be associated with any class.
+/////////////////////////////////
+// Error Logging Macros
+// See top of file for common usage.
+/////////////////////////////////
+// this macro uses a one-shot do statement to avoid parsing errors when writing control flow statements
+// without braces:
+// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL
+#define lllog(level, once, ...) \
+ do { \
+ const char* tags[] = {"", ##__VA_ARGS__}; \
+ ::size_t tag_count = LL_ARRAY_SIZE(tags) - 1; \
+ static LLError::CallSite _site( \
+ level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, once, &tags[1], tag_count);\
+ if (LL_UNLIKELY(_site.shouldLog())) \
+ { \
+ std::ostringstream* _out = LLError::Log::out(); \
+ (*_out)
+//Use this construct if you need to do computation in the middle of a
+//message:
+//
+// LL_INFOS("AgentGesture") << "the agent " << agend_id;
+// switch (f)
+// {
+// case FOP_SHRUGS: LL_CONT << "shrugs"; break;
+// case FOP_TAPS: LL_CONT << "points at " << who; break;
+// case FOP_SAYS: LL_CONT << "says " << message; break;
+// }
+// LL_CONT << " for " << t << " seconds" << LL_ENDL;
+//
+//Such computation is done iff the message will be logged.
+#define LL_CONT (*_out)
-/*
- Error Logging Macros
- See top of file for common usage.
-*/
+#define LL_NEWLINE '\n'
-#define lllog(level, broadTag, narrowTag, once) \
- do { \
- static LLError::CallSite _site( \
- level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, broadTag, narrowTag, once);\
- if (LL_UNLIKELY(_site.shouldLog())) \
- { \
- std::ostringstream* _out = LLError::Log::out(); \
- (*_out)
-
-// DEPRECATED: Don't call directly, use LL_ENDL instead, which actually looks like a macro
-#define llendl \
- LLError::End(); \
+#define LL_ENDL \
+ LLError::End(); \
LLError::Log::flush(_out, _site); \
- } \
+ } \
} while(0)
-// DEPRECATED: Use the new macros that allow tags and *look* like macros.
-#define lldebugs lllog(LLError::LEVEL_DEBUG, NULL, NULL, false)
-#define llinfos lllog(LLError::LEVEL_INFO, NULL, NULL, false)
-#define llwarns lllog(LLError::LEVEL_WARN, NULL, NULL, false)
-#define llerrs lllog(LLError::LEVEL_ERROR, NULL, NULL, false)
-#define llcont (*_out)
-
// NEW Macros for debugging, allow the passing of a string tag
-// One Tag
-#define LL_DEBUGS(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, false)
-#define LL_INFOS(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, false)
-#define LL_WARNS(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, false)
-#define LL_ERRS(broadTag) lllog(LLError::LEVEL_ERROR, broadTag, NULL, false)
-// Two Tags
-#define LL_DEBUGS2(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, false)
-#define LL_INFOS2(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, false)
-#define LL_WARNS2(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, false)
-#define LL_ERRS2(broadTag, narrowTag) lllog(LLError::LEVEL_ERROR, broadTag, narrowTag, false)
+// Pass comma separated list of tags (currently only supports up to 0, 1, or 2)
+#define LL_DEBUGS(...) lllog(LLError::LEVEL_DEBUG, false, ##__VA_ARGS__)
+#define LL_INFOS(...) lllog(LLError::LEVEL_INFO, false, ##__VA_ARGS__)
+#define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__)
+#define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__)
+// alternative to llassert_always that prints explanatory message
+#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(##__VA_ARGS__) << "(" #exp ")"
// Only print the log message once (good for warnings or infos that would otherwise
// spam the log file over and over, such as tighter loops).
-#define LL_DEBUGS_ONCE(broadTag) lllog(LLError::LEVEL_DEBUG, broadTag, NULL, true)
-#define LL_INFOS_ONCE(broadTag) lllog(LLError::LEVEL_INFO, broadTag, NULL, true)
-#define LL_WARNS_ONCE(broadTag) lllog(LLError::LEVEL_WARN, broadTag, NULL, true)
-#define LL_DEBUGS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_DEBUG, broadTag, narrowTag, true)
-#define LL_INFOS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_INFO, broadTag, narrowTag, true)
-#define LL_WARNS2_ONCE(broadTag, narrowTag) lllog(LLError::LEVEL_WARN, broadTag, narrowTag, true)
-
-#define LL_ENDL llendl
-#define LL_CONT (*_out)
+#define LL_DEBUGS_ONCE(...) lllog(LLError::LEVEL_DEBUG, true, ##__VA_ARGS__)
+#define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__)
+#define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__)
- /*
- Use this construct if you need to do computation in the middle of a
- message:
-
- LL_INFOS("AgentGesture") << "the agent " << agend_id;
- switch (f)
- {
- case FOP_SHRUGS: LL_CONT << "shrugs"; break;
- case FOP_TAPS: LL_CONT << "points at " << who; break;
- case FOP_SAYS: LL_CONT << "says " << message; break;
- }
- LL_CONT << " for " << t << " seconds" << LL_ENDL;
-
- Such computation is done iff the message will be logged.
- */
+// DEPRECATED: Use the new macros that allow tags and *look* like macros.
+#define lldebugs LL_COMPILE_TIME_MESSAGE("Warning: lldebugs deprecated, use LL_DEBUGS() instead") LL_DEBUGS()
+#define llinfos LL_COMPILE_TIME_MESSAGE("Warning: llinfos deprecated, use LL_INFOS() instead") LL_INFOS()
+#define llwarns LL_COMPILE_TIME_MESSAGE("Warning: llwarns deprecated, use LL_WARNS() instead") LL_WARNS()
+#define llerrs LL_COMPILE_TIME_MESSAGE("Warning: llerrs deprecated, use LL_ERRS() instead") LL_ERRS()
+#define llcont LL_COMPILE_TIME_MESSAGE("Warning: llcont deprecated, use LL_CONT instead") LL_CONT
+#define llendl LL_COMPILE_TIME_MESSAGE("Warning: llendl deprecated, use LL_ENDL instead") LL_ENDL
#endif // LL_LLERROR_H
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index 233e9d3389..56ac52e5de 100644..100755
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -1,33 +1,27 @@
-/**
+/**
* @file llerrorcontrol.h
* @date December 2006
* @brief error message system control
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,7 +29,10 @@
#define LL_LLERRORCONTROL_H
#include "llerror.h"
+#include "llpointer.h"
+#include "llrefcount.h"
#include "boost/function.hpp"
+#include "boost/shared_ptr.hpp"
#include <string>
class LLSD;
@@ -44,7 +41,7 @@ class LLSD;
This is the part of the LLError namespace that manages the messages
produced by the logging. The logging support is defined in llerror.h.
Most files do not need to include this.
-
+
These implementations are in llerror.cpp.
*/
@@ -68,7 +65,7 @@ namespace LLError
// logs to stderr, syslog, and windows debug log
// the identity string is used for in the syslog
- LL_COMMON_API void initForApplication(const std::string& dir);
+ LL_COMMON_API void initForApplication(const std::string& dir, bool log_to_stderr = true);
// resets all logging settings to defaults needed by applicaitons
// logs to stderr and windows debug log
// sets up log configuration from the file logcontrol.xml in dir
@@ -78,14 +75,16 @@ namespace LLError
Settings that control what is logged.
Setting a level means log messages at that level or above.
*/
-
+
LL_COMMON_API void setPrintLocation(bool);
LL_COMMON_API void setDefaultLevel(LLError::ELevel);
+ LL_COMMON_API ELevel getDefaultLevel();
LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel);
LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel);
LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel);
LL_COMMON_API void setTagLevel(const std::string& file_name, LLError::ELevel);
-
+
+ LL_COMMON_API LLError::ELevel decodeLevel(std::string name);
LL_COMMON_API void configure(const LLSD&);
// the LLSD can configure all of the settings
// usually read automatically from the live errorlog.xml file
@@ -105,31 +104,31 @@ namespace LLError
// (by, for example, setting a class level to LEVEL_NONE), will keep
// the that message from causing the fatal funciton to be invoked.
- LL_COMMON_API FatalFunction getFatalFunction();
- // Retrieve the previously-set FatalFunction
-
- /// temporarily override the FatalFunction for the duration of a
- /// particular scope, e.g. for unit tests
- class LL_COMMON_API OverrideFatalFunction
- {
- public:
- OverrideFatalFunction(const FatalFunction& func):
- mPrev(getFatalFunction())
- {
- setFatalFunction(func);
- }
- ~OverrideFatalFunction()
- {
- setFatalFunction(mPrev);
- }
-
- private:
- FatalFunction mPrev;
- };
+ LL_COMMON_API FatalFunction getFatalFunction();
+ // Retrieve the previously-set FatalFunction
+
+ /// temporarily override the FatalFunction for the duration of a
+ /// particular scope, e.g. for unit tests
+ class LL_COMMON_API OverrideFatalFunction
+ {
+ public:
+ OverrideFatalFunction(const FatalFunction& func):
+ mPrev(getFatalFunction())
+ {
+ setFatalFunction(func);
+ }
+ ~OverrideFatalFunction()
+ {
+ setFatalFunction(mPrev);
+ }
+
+ private:
+ FatalFunction mPrev;
+ };
typedef std::string (*TimeFunction)();
LL_COMMON_API std::string utcTime();
-
+
LL_COMMON_API void setTimeFunction(TimeFunction);
// The function is use to return the current time, formatted for
// display by those error recorders that want the time included.
@@ -140,20 +139,36 @@ namespace LLError
{
// An object that handles the actual output or error messages.
public:
+ Recorder();
virtual ~Recorder();
-
+
virtual void recordMessage(LLError::ELevel, const std::string& message) = 0;
// use the level for better display, not for filtering
-
- virtual bool wantsTime(); // default returns false
- // override and return true if the recorder wants the time string
- // included in the text of the message
+
+ bool wantsTime();
+ bool wantsTags();
+ bool wantsLevel();
+ bool wantsLocation();
+ bool wantsFunctionName();
+
+ protected:
+ bool mWantsTime,
+ mWantsTags,
+ mWantsLevel,
+ mWantsLocation,
+ mWantsFunctionName;
};
-
- LL_COMMON_API void addRecorder(Recorder*);
- LL_COMMON_API void removeRecorder(Recorder*);
+
+ typedef boost::shared_ptr<Recorder> RecorderPtr;
+
+ /**
+ * @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership
+ * while still ensuring that the allocated memory is eventually freed
+ */
+ LL_COMMON_API void addRecorder(RecorderPtr);
+ LL_COMMON_API void removeRecorder(RecorderPtr);
// each error message is passed to each recorder via recordMessage()
-
+
LL_COMMON_API void logToFile(const std::string& filename);
LL_COMMON_API void logToFixedBuffer(LLLineBuffer*);
// Utilities to add recorders for logging to a file or a fixed buffer
@@ -168,13 +183,12 @@ namespace LLError
Utilities for use by the unit tests of LLError itself.
*/
- class Settings;
- LL_COMMON_API Settings* saveAndResetSettings();
- LL_COMMON_API void restoreSettings(Settings *);
-
+ typedef LLPointer<LLRefCount> SettingsStoragePtr;
+ LL_COMMON_API SettingsStoragePtr saveAndResetSettings();
+ LL_COMMON_API void restoreSettings(SettingsStoragePtr pSettingsStorage);
+
LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
LL_COMMON_API int shouldLogCallCount();
-
};
#endif // LL_LLERRORCONTROL_H
diff --git a/indra/llcommon/llerrorlegacy.h b/indra/llcommon/llerrorlegacy.h
index 476d75380f..31dd207008 100644..100755
--- a/indra/llcommon/llerrorlegacy.h
+++ b/indra/llcommon/llerrorlegacy.h
@@ -3,122 +3,30 @@
* @date January 2007
* @brief old things from the older error system
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLERRORLEGACY_H
#define LL_LLERRORLEGACY_H
-#include "llpreprocessor.h"
-
-/*
- LEGACY -- DO NOT USE THIS STUFF ANYMORE
-*/
-
-// Specific error codes
-const int LL_ERR_NOERR = 0;
-const int LL_ERR_ASSET_REQUEST_FAILED = -1;
-//const int LL_ERR_ASSET_REQUEST_INVALID = -2;
-const int LL_ERR_ASSET_REQUEST_NONEXISTENT_FILE = -3;
-const int LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE = -4;
-const int LL_ERR_INSUFFICIENT_PERMISSIONS = -5;
-const int LL_ERR_EOF = -39;
-const int LL_ERR_CANNOT_OPEN_FILE = -42;
-const int LL_ERR_FILE_NOT_FOUND = -43;
-const int LL_ERR_FILE_EMPTY = -44;
-const int LL_ERR_TCP_TIMEOUT = -23016;
-const int LL_ERR_CIRCUIT_GONE = -23017;
-const int LL_ERR_PRICE_MISMATCH = -23018;
-
-
-
-// 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 LL_RELEASE_WITH_DEBUG_INFO
-#define SHOW_ASSERT
-#endif // LL_RELEASE_WITH_DEBUG_INFO
-
-#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
-
-
-
-#define lldebugst(type) lldebugs
-#define llendflush llendl
-
-
-#define llerror(msg, num) llerrs << "Error # " << num << ": " << msg << llendl;
-
-#define llwarning(msg, num) llwarns << "Warning # " << num << ": " << msg << llendl;
-
-#define llassert_always(func) if (LL_UNLIKELY(!(func))) llerrs << "ASSERT (" << #func << ")" << llendl;
-
-#ifdef SHOW_ASSERT
-#define llassert(func) llassert_always(func)
-#define llverify(func) llassert_always(func)
-#else
-#define llassert(func)
-#define llverify(func) do {if (func) {}} while(0)
-#endif
-
-// handy compile-time assert - enforce those template parameters!
-#define cassert(expn) typedef char __C_ASSERT__[(expn)?1:-1] /* Flawfinder: ignore */
- //XXX: used in two places in llcommon/llskipmap.h
#endif // LL_LLERRORLEGACY_H
diff --git a/indra/llcommon/llerrorthread.cpp b/indra/llcommon/llerrorthread.cpp
index f0e46ae78d..f6bc68b5c1 100644..100755
--- a/indra/llcommon/llerrorthread.cpp
+++ b/indra/llcommon/llerrorthread.cpp
@@ -1,31 +1,25 @@
/**
* @file llerrorthread.cpp
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -71,7 +65,7 @@ void get_child_status(const int waitpid_status, int &process_status, bool &exite
exited = true;
if (do_logging)
{
- llinfos << "get_child_status - Child exited cleanly with return of " << process_status << llendl;
+ LL_INFOS() << "get_child_status - Child exited cleanly with return of " << process_status << LL_ENDL;
}
return;
}
@@ -81,15 +75,15 @@ void get_child_status(const int waitpid_status, int &process_status, bool &exite
exited = true;
if (do_logging)
{
- llinfos << "get_child_status - Child died because of uncaught signal " << process_status << llendl;
+ LL_INFOS() << "get_child_status - Child died because of uncaught signal " << process_status << LL_ENDL;
#ifdef WCOREDUMP
if (WCOREDUMP(waitpid_status))
{
- llinfos << "get_child_status - Child dumped core" << llendl;
+ LL_INFOS() << "get_child_status - Child dumped core" << LL_ENDL;
}
else
{
- llinfos << "get_child_status - Child didn't dump core" << llendl;
+ LL_INFOS() << "get_child_status - Child didn't dump core" << LL_ENDL;
}
#endif
}
@@ -99,7 +93,7 @@ void get_child_status(const int waitpid_status, int &process_status, bool &exite
{
// 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;
+ LL_INFOS() << "get_child_status - Got SIGCHILD but child didn't exit" << LL_ENDL;
process_status = waitpid_status;
}
@@ -112,104 +106,28 @@ void LLErrorThread::run()
// 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;
+ LL_INFOS() << "thread_error - Waiting for an error" << LL_ENDL;
S32 counter = 0;
-#if !LL_WINDOWS
- U32 last_sig_child_count = 0;
-#endif
- while (1)
+ while (! (LLApp::isError() || LLApp::isStopped()))
{
- 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;
+ //LL_INFOS() << "thread_error - An error has occurred, running error callback!" << LL_ENDL;
// Run the error handling callback
LLApp::runErrorHandler();
}
else
{
// Everything is okay, a clean exit.
- //llinfos << "thread_error - Application exited cleanly" << llendl;
+ //LL_INFOS() << "thread_error - Application exited cleanly" << LL_ENDL;
}
- //llinfos << "thread_error - Exiting" << llendl;
+ //LL_INFOS() << "thread_error - Exiting" << LL_ENDL;
LLApp::sErrorThreadRunning = FALSE;
}
diff --git a/indra/llcommon/llerrorthread.h b/indra/llcommon/llerrorthread.h
index 3121d29675..474cef3a50 100644..100755
--- a/indra/llcommon/llerrorthread.h
+++ b/indra/llcommon/llerrorthread.h
@@ -2,31 +2,25 @@
* @file llerrorthread.h
* @brief Specialized thread to handle runtime errors.
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llevent.cpp b/indra/llcommon/llevent.cpp
index f669d0e13f..633df01588 100644..100755
--- a/indra/llcommon/llevent.cpp
+++ b/indra/llcommon/llevent.cpp
@@ -2,31 +2,25 @@
* @file llevent.cpp
* @brief LLEvent and LLEventListener base classes.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h
index 0ea7cf4ae8..28ce7de102 100644..100755
--- a/indra/llcommon/llevent.h
+++ b/indra/llcommon/llevent.h
@@ -3,31 +3,25 @@
* @author Tom Yedwab
* @brief LLEvent and LLEventListener base classes.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,8 +29,8 @@
#define LL_EVENT_H
#include "llsd.h"
+#include "llrefcount.h"
#include "llpointer.h"
-#include "llthread.h"
namespace LLOldEvents
{
diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp
index 1dd104da8f..ff5459c1eb 100644..100755
--- a/indra/llcommon/lleventapi.cpp
+++ b/indra/llcommon/lleventapi.cpp
@@ -4,8 +4,25 @@
* @date 2009-11-10
* @brief Implementation for lleventapi.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -17,6 +34,7 @@
// std headers
// external library headers
// other Linden headers
+#include "llerror.h"
LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const std::string& field):
lbase(name, field),
@@ -28,3 +46,32 @@ LLEventAPI::LLEventAPI(const std::string& name, const std::string& desc, const s
LLEventAPI::~LLEventAPI()
{
}
+
+LLEventAPI::Response::Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey):
+ mResp(seed),
+ mReq(request),
+ mKey(replyKey)
+{}
+
+LLEventAPI::Response::~Response()
+{
+ // When you instantiate a stack Response object, if the original
+ // request requested a reply, send it when we leave this block, no
+ // matter how.
+ sendReply(mResp, mReq, mKey);
+}
+
+void LLEventAPI::Response::warn(const std::string& warning)
+{
+ LL_WARNS("LLEventAPI::Response") << warning << LL_ENDL;
+ mResp["warnings"].append(warning);
+}
+
+void LLEventAPI::Response::error(const std::string& error)
+{
+ // Use LL_WARNS rather than LL_ERROR: we don't want the viewer to shut
+ // down altogether.
+ LL_WARNS("LLEventAPI::Response") << error << LL_ENDL;
+
+ mResp["error"] = error;
+}
diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h
index 96d1b03be8..5991fe8fd5 100644..100755
--- a/indra/llcommon/lleventapi.h
+++ b/indra/llcommon/lleventapi.h
@@ -6,8 +6,25 @@
* in an event API
* (see https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API).
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -30,6 +47,7 @@ class LL_COMMON_API LLEventAPI: public LLDispatchListener,
typedef LLInstanceTracker<LLEventAPI, std::string> ibase;
public:
+
/**
* @param name LLEventPump name on which this LLEventAPI will listen. This
* also serves as the LLInstanceTracker instance key.
@@ -59,6 +77,89 @@ public:
LLEventDispatcher::add(name, desc, callable, required);
}
+ /**
+ * Instantiate a Response object in any LLEventAPI subclass method that
+ * wants to guarantee a reply (if requested) will be sent on exit from the
+ * method. The reply will be sent if request.has(@a replyKey), default
+ * "reply". If specified, the value of request[replyKey] is the name of
+ * the LLEventPump on which to send the reply. Conventionally you might
+ * code something like:
+ *
+ * @code
+ * void MyEventAPI::someMethod(const LLSD& request)
+ * {
+ * // Send a reply event as long as request.has("reply")
+ * Response response(LLSD(), request);
+ * // ...
+ * // will be sent in reply event
+ * response["somekey"] = some_data;
+ * }
+ * @endcode
+ */
+ class LL_COMMON_API Response
+ {
+ public:
+ /**
+ * Instantiating a Response object in an LLEventAPI subclass method
+ * ensures that, if desired, a reply event will be sent.
+ *
+ * @a seed is the initial reply LLSD that will be further decorated before
+ * being sent as the reply
+ *
+ * @a request is the incoming request LLSD; we particularly care about
+ * [replyKey] and ["reqid"]
+ *
+ * @a replyKey [default "reply"] is the string name of the LLEventPump
+ * on which the caller wants a reply. If <tt>(!
+ * request.has(replyKey))</tt>, no reply will be sent.
+ */
+ Response(const LLSD& seed, const LLSD& request, const LLSD::String& replyKey="reply");
+ ~Response();
+
+ /**
+ * @code
+ * if (some condition)
+ * {
+ * response.warn("warnings are logged and collected in [\"warnings\"]");
+ * }
+ * @endcode
+ */
+ void warn(const std::string& warning);
+ /**
+ * @code
+ * if (some condition isn't met)
+ * {
+ * // In a function returning void, you can validly 'return
+ * // expression' if the expression is itself of type void. But
+ * // returning is up to you; response.error() has no effect on
+ * // flow of control.
+ * return response.error("error message, logged and also sent as [\"error\"]");
+ * }
+ * @endcode
+ */
+ void error(const std::string& error);
+
+ /**
+ * set other keys...
+ *
+ * @code
+ * // set any attributes you want to be sent in the reply
+ * response["info"] = some_value;
+ * // ...
+ * response["ok"] = went_well;
+ * @endcode
+ */
+ LLSD& operator[](const LLSD::String& key) { return mResp[key]; }
+
+ /**
+ * set the response to the given data
+ */
+ void setResponse(LLSD const & response){ mResp = response; }
+
+ LLSD mResp, mReq;
+ LLSD::String mKey;
+ };
+
private:
std::string mDesc;
};
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index d598f1cc4a..81cc33fbba 100644..100755
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -4,8 +4,25 @@
* @date 2009-04-29
* @brief Implementation for lleventcoro.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index 1981ae7482..abbeeaa373 100644..100755
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -4,16 +4,33 @@
* @date 2009-04-29
* @brief Utilities to interface between coroutines and events.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
-#include <boost/coroutine/coroutine.hpp>
-#include <boost/coroutine/future.hpp>
+#include <boost/dcoroutine/coroutine.hpp>
+#include <boost/dcoroutine/future.hpp>
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
@@ -50,7 +67,7 @@ public:
LLEventPumpOrPumpName() {}
operator LLEventPump& () const { return *mPump; }
LLEventPump& getPump() const { return *mPump; }
- operator bool() const { return mPump; }
+ operator bool() const { return bool(mPump); }
bool operator!() const { return ! mPump; }
private:
@@ -85,6 +102,9 @@ LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
namespace LLEventDetail
{
+ /// Implementation for listenerNameForCoro(), see below
+ LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
+
/**
* waitForEventOn() permits a coroutine to temporarily listen on an
* LLEventPump any number of times. We don't really want to have to ask
@@ -112,9 +132,6 @@ namespace LLEventDetail
return listenerNameForCoroImpl(self.get_id());
}
- /// Implementation for listenerNameForCoro()
- LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
-
/**
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
* parameter:
@@ -189,13 +206,13 @@ LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& req
const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
{
// declare the future
- boost::coroutines::future<LLSD> future(self);
+ boost::dcoroutines::future<LLSD> future(self);
// make a callback that will assign a value to the future, and listen on
// the specified LLEventPump with that callback
std::string listenerName(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection(
replyPump.getPump().listen(listenerName,
- voidlistener(boost::coroutines::make_callback(future))));
+ voidlistener(boost::dcoroutines::make_callback(future))));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
@@ -240,7 +257,7 @@ namespace LLEventDetail
* This helper is specifically for the two-pump version of waitForEventOn().
* We use a single future object, but we want to listen on two pumps with it.
* Since we must still adapt from (the callable constructed by)
- * boost::coroutines::make_callback() (void return) to provide an event
+ * boost::dcoroutines::make_callback() (void return) to provide an event
* listener (bool return), we've adapted LLVoidListener for the purpose. The
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
* -- binding different instance data -- for each of the pumps. Then, when a
@@ -314,16 +331,16 @@ LLEventWithID postAndWait2(SELF& self, const LLSD& event,
const LLSD& replyPump1NamePath=LLSD())
{
// declare the future
- boost::coroutines::future<LLEventWithID> future(self);
+ boost::dcoroutines::future<LLEventWithID> future(self);
// either callback will assign a value to this future; listen on
// each specified LLEventPump with a callback
std::string name(LLEventDetail::listenerNameForCoro(self));
LLTempBoundListener connection0(
replyPump0.getPump().listen(name + "a",
- LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
+ LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0)));
LLTempBoundListener connection1(
replyPump1.getPump().listen(name + "b",
- LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
+ LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1)));
// skip the "post" part if requestPump is default-constructed
if (requestPump)
{
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
index 5fa6059718..5b6d4efbe9 100644..100755
--- a/indra/llcommon/lleventdispatcher.cpp
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -4,8 +4,25 @@
* @date 2009-06-18
* @brief Implementation for lleventdispatcher.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -24,6 +41,354 @@
#include "llevents.h"
#include "llerror.h"
#include "llsdutil.h"
+#include "stringize.h"
+#include <memory> // std::auto_ptr
+
+/*****************************************************************************
+* LLSDArgsSource
+*****************************************************************************/
+/**
+ * Store an LLSD array, producing its elements one at a time. Die with LL_ERRS
+ * if the consumer requests more elements than the array contains.
+ */
+class LL_COMMON_API LLSDArgsSource
+{
+public:
+ LLSDArgsSource(const std::string function, const LLSD& args);
+ ~LLSDArgsSource();
+
+ LLSD next();
+
+ void done() const;
+
+private:
+ std::string _function;
+ LLSD _args;
+ LLSD::Integer _index;
+};
+
+LLSDArgsSource::LLSDArgsSource(const std::string function, const LLSD& args):
+ _function(function),
+ _args(args),
+ _index(0)
+{
+ if (! (_args.isUndefined() || _args.isArray()))
+ {
+ LL_ERRS("LLSDArgsSource") << _function << " needs an args array instead of "
+ << _args << LL_ENDL;
+ }
+}
+
+LLSDArgsSource::~LLSDArgsSource()
+{
+ done();
+}
+
+LLSD LLSDArgsSource::next()
+{
+ if (_index >= _args.size())
+ {
+ LL_ERRS("LLSDArgsSource") << _function << " requires more arguments than the "
+ << _args.size() << " provided: " << _args << LL_ENDL;
+ }
+ return _args[_index++];
+}
+
+void LLSDArgsSource::done() const
+{
+ if (_index < _args.size())
+ {
+ LL_WARNS("LLSDArgsSource") << _function << " only consumed " << _index
+ << " of the " << _args.size() << " arguments provided: "
+ << _args << LL_ENDL;
+ }
+}
+
+/*****************************************************************************
+* LLSDArgsMapper
+*****************************************************************************/
+/**
+ * From a formal parameters description and a map of arguments, construct an
+ * arguments array.
+ *
+ * That is, given:
+ * - an LLSD array of length n containing parameter-name strings,
+ * corresponding to the arguments of a function of interest
+ * - an LLSD collection specifying default parameter values, either:
+ * - an LLSD array of length m <= n, matching the rightmost m params, or
+ * - an LLSD map explicitly stating default name=value pairs
+ * - an LLSD map of parameter names and actual values for a particular
+ * function call
+ * construct an LLSD array of actual argument values for this function call.
+ *
+ * The parameter-names array and the defaults collection describe the function
+ * being called. The map might vary with every call, providing argument values
+ * for the described parameters.
+ *
+ * The array of parameter names must match the number of parameters expected
+ * by the function of interest.
+ *
+ * If you pass a map of default parameter values, it provides default values
+ * as you might expect. It is an error to specify a default value for a name
+ * not listed in the parameters array.
+ *
+ * If you pass an array of default parameter values, it is mapped to the
+ * rightmost m of the n parameter names. It is an error if the default-values
+ * array is longer than the parameter-names array. Consider the following
+ * parameter names: ["a", "b", "c", "d"].
+ *
+ * - An empty array of default values (or an isUndefined() value) asserts that
+ * every one of the above parameter names is required.
+ * - An array of four default values [1, 2, 3, 4] asserts that every one of
+ * the above parameters is optional. If the current parameter map is empty,
+ * they will be passed to the function as [1, 2, 3, 4].
+ * - An array of two default values [11, 12] asserts that parameters "a" and
+ * "b" are required, while "c" and "d" are optional, having default values
+ * "c"=11 and "d"=12.
+ *
+ * The arguments array is constructed as follows:
+ *
+ * - Arguments-map keys not found in the parameter-names array are ignored.
+ * - Entries from the map provide values for an improper subset of the
+ * parameters named in the parameter-names array. This results in a
+ * tentative values array with "holes." (size of map) + (number of holes) =
+ * (size of names array)
+ * - Holes are filled with the default values.
+ * - Any remaining holes constitute an error.
+ */
+class LL_COMMON_API LLSDArgsMapper
+{
+public:
+ /// Accept description of function: function name, param names, param
+ /// default values
+ LLSDArgsMapper(const std::string& function, const LLSD& names, const LLSD& defaults);
+
+ /// Given arguments map, return LLSD::Array of parameter values, or LL_ERRS.
+ LLSD map(const LLSD& argsmap) const;
+
+private:
+ static std::string formatlist(const LLSD&);
+
+ // The function-name string is purely descriptive. We want error messages
+ // to be able to indicate which function's LLSDArgsMapper has the problem.
+ std::string _function;
+ // Store the names array pretty much as given.
+ LLSD _names;
+ // Though we're handed an array of name strings, it's more useful to us to
+ // store it as a map from name string to position index. Of course that's
+ // easy to generate from the incoming names array, but why do it more than
+ // once?
+ typedef std::map<LLSD::String, LLSD::Integer> IndexMap;
+ IndexMap _indexes;
+ // Generated array of default values, aligned with the array of param names.
+ LLSD _defaults;
+ // Indicate whether we have a default value for each param.
+ typedef std::vector<char> FilledVector;
+ FilledVector _has_dft;
+};
+
+LLSDArgsMapper::LLSDArgsMapper(const std::string& function,
+ const LLSD& names, const LLSD& defaults):
+ _function(function),
+ _names(names),
+ _has_dft(names.size())
+{
+ if (! (_names.isUndefined() || _names.isArray()))
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " names must be an array, not " << names << LL_ENDL;
+ }
+ LLSD::Integer nparams(_names.size());
+ // From _names generate _indexes.
+ for (LLSD::Integer ni = 0, nend = _names.size(); ni < nend; ++ni)
+ {
+ _indexes[_names[ni]] = ni;
+ }
+
+ // Presize _defaults() array so we don't have to resize it more than once.
+ // All entries are initialized to LLSD(); but since _has_dft is still all
+ // 0, they're all "holes" for now.
+ if (nparams)
+ {
+ _defaults[nparams - 1] = LLSD();
+ }
+
+ if (defaults.isUndefined() || defaults.isArray())
+ {
+ LLSD::Integer ndefaults = defaults.size();
+ // defaults is a (possibly empty) array. Right-align it with names.
+ if (ndefaults > nparams)
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " names array " << names
+ << " shorter than defaults array " << defaults << LL_ENDL;
+ }
+
+ // Offset by which we slide defaults array right to right-align with
+ // _names array
+ LLSD::Integer offset = nparams - ndefaults;
+ // Fill rightmost _defaults entries from defaults, and mark them as
+ // filled
+ for (LLSD::Integer i = 0, iend = ndefaults; i < iend; ++i)
+ {
+ _defaults[i + offset] = defaults[i];
+ _has_dft[i + offset] = 1;
+ }
+ }
+ else if (defaults.isMap())
+ {
+ // defaults is a map. Use it to populate the _defaults array.
+ LLSD bogus;
+ for (LLSD::map_const_iterator mi(defaults.beginMap()), mend(defaults.endMap());
+ mi != mend; ++mi)
+ {
+ IndexMap::const_iterator ixit(_indexes.find(mi->first));
+ if (ixit == _indexes.end())
+ {
+ bogus.append(mi->first);
+ continue;
+ }
+
+ LLSD::Integer pos = ixit->second;
+ // Store default value at that position in the _defaults array.
+ _defaults[pos] = mi->second;
+ // Don't forget to record the fact that we've filled this
+ // position.
+ _has_dft[pos] = 1;
+ }
+ if (bogus.size())
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " defaults specified for nonexistent params "
+ << formatlist(bogus) << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS("LLSDArgsMapper") << function << " defaults must be a map or an array, not "
+ << defaults << LL_ENDL;
+ }
+}
+
+LLSD LLSDArgsMapper::map(const LLSD& argsmap) const
+{
+ if (! (argsmap.isUndefined() || argsmap.isMap() || argsmap.isArray()))
+ {
+ LL_ERRS("LLSDArgsMapper") << _function << " map() needs a map or array, not "
+ << argsmap << LL_ENDL;
+ }
+ // Initialize the args array. Indexing a non-const LLSD array grows it
+ // to appropriate size, but we don't want to resize this one on each
+ // new operation. Just make it as big as we need before we start
+ // stuffing values into it.
+ LLSD args(LLSD::emptyArray());
+ if (_defaults.size() == 0)
+ {
+ // If this function requires no arguments, fast exit. (Don't try to
+ // assign to args[-1].)
+ return args;
+ }
+ args[_defaults.size() - 1] = LLSD();
+
+ // Get a vector of chars to indicate holes. It's tempting to just scan
+ // for LLSD::isUndefined() values after filling the args array from
+ // the map, but it's plausible for caller to explicitly pass
+ // isUndefined() as the value of some parameter name. That's legal
+ // since isUndefined() has well-defined conversions (default value)
+ // for LLSD data types. So use a whole separate array for detecting
+ // holes. (Avoid std::vector<bool> which is known to be odd -- can we
+ // iterate?)
+ FilledVector filled(args.size());
+
+ if (argsmap.isArray())
+ {
+ // Fill args from array. If there are too many args in passed array,
+ // ignore the rest.
+ LLSD::Integer size(argsmap.size());
+ if (size > args.size())
+ {
+ // We don't just use std::min() because we want to sneak in this
+ // warning if caller passes too many args.
+ LL_WARNS("LLSDArgsMapper") << _function << " needs " << args.size()
+ << " params, ignoring last " << (size - args.size())
+ << " of passed " << size << ": " << argsmap << LL_ENDL;
+ size = args.size();
+ }
+ for (LLSD::Integer i(0); i < size; ++i)
+ {
+ // Copy the actual argument from argsmap
+ args[i] = argsmap[i];
+ // Note that it's been filled
+ filled[i] = 1;
+ }
+ }
+ else
+ {
+ // argsmap is in fact a map. Walk the map.
+ for (LLSD::map_const_iterator mi(argsmap.beginMap()), mend(argsmap.endMap());
+ mi != mend; ++mi)
+ {
+ // mi->first is a parameter-name string, with mi->second its
+ // value. Look up the name's position index in _indexes.
+ IndexMap::const_iterator ixit(_indexes.find(mi->first));
+ if (ixit == _indexes.end())
+ {
+ // Allow for a map containing more params than were passed in
+ // our names array. Caller typically receives a map containing
+ // the function name, cruft such as reqid, etc. Ignore keys
+ // not defined in _indexes.
+ LL_DEBUGS("LLSDArgsMapper") << _function << " ignoring "
+ << mi->first << "=" << mi->second << LL_ENDL;
+ continue;
+ }
+ LLSD::Integer pos = ixit->second;
+ // Store the value at that position in the args array.
+ args[pos] = mi->second;
+ // Don't forget to record the fact that we've filled this
+ // position.
+ filled[pos] = 1;
+ }
+ }
+
+ // Fill any remaining holes from _defaults.
+ LLSD unfilled(LLSD::emptyArray());
+ for (LLSD::Integer i = 0, iend = args.size(); i < iend; ++i)
+ {
+ if (! filled[i])
+ {
+ // If there's no default value for this parameter, that's an
+ // error.
+ if (! _has_dft[i])
+ {
+ unfilled.append(_names[i]);
+ }
+ else
+ {
+ args[i] = _defaults[i];
+ }
+ }
+ }
+ // If any required args -- args without defaults -- were left unfilled
+ // by argsmap, that's a problem.
+ if (unfilled.size())
+ {
+ LL_ERRS("LLSDArgsMapper") << _function << " missing required arguments "
+ << formatlist(unfilled) << " from " << argsmap << LL_ENDL;
+ }
+
+ // done
+ return args;
+}
+
+std::string LLSDArgsMapper::formatlist(const LLSD& list)
+{
+ std::ostringstream out;
+ const char* delim = "";
+ for (LLSD::array_const_iterator li(list.beginArray()), lend(list.endArray());
+ li != lend; ++li)
+ {
+ out << delim << li->asString();
+ delim = ", ";
+ }
+ return out.str();
+}
LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
mDesc(desc),
@@ -35,12 +400,181 @@ LLEventDispatcher::~LLEventDispatcher()
{
}
+/**
+ * DispatchEntry subclass used for callables accepting(const LLSD&)
+ */
+struct LLEventDispatcher::LLSDDispatchEntry: public LLEventDispatcher::DispatchEntry
+{
+ LLSDDispatchEntry(const std::string& desc, const Callable& func, const LLSD& required):
+ DispatchEntry(desc),
+ mFunc(func),
+ mRequired(required)
+ {}
+
+ Callable mFunc;
+ LLSD mRequired;
+
+ virtual void call(const std::string& desc, const LLSD& event) const
+ {
+ // Validate the syntax of the event itself.
+ std::string mismatch(llsd_matches(mRequired, event));
+ if (! mismatch.empty())
+ {
+ LL_ERRS("LLEventDispatcher") << desc << ": bad request: " << mismatch << LL_ENDL;
+ }
+ // Event syntax looks good, go for it!
+ mFunc(event);
+ }
+
+ virtual LLSD addMetadata(LLSD meta) const
+ {
+ meta["required"] = mRequired;
+ return meta;
+ }
+};
+
+/**
+ * DispatchEntry subclass for passing LLSD to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::ParamsDispatchEntry: public LLEventDispatcher::DispatchEntry
+{
+ ParamsDispatchEntry(const std::string& desc, const invoker_function& func):
+ DispatchEntry(desc),
+ mInvoker(func)
+ {}
+
+ invoker_function mInvoker;
+
+ virtual void call(const std::string& desc, const LLSD& event) const
+ {
+ LLSDArgsSource src(desc, event);
+ mInvoker(boost::bind(&LLSDArgsSource::next, boost::ref(src)));
+ }
+};
+
+/**
+ * DispatchEntry subclass for dispatching LLSD::Array to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::ArrayParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
+{
+ ArrayParamsDispatchEntry(const std::string& desc, const invoker_function& func,
+ LLSD::Integer arity):
+ ParamsDispatchEntry(desc, func),
+ mArity(arity)
+ {}
+
+ LLSD::Integer mArity;
+
+ virtual LLSD addMetadata(LLSD meta) const
+ {
+ LLSD array(LLSD::emptyArray());
+ // Resize to number of arguments required
+ if (mArity)
+ array[mArity - 1] = LLSD();
+ llassert_always(array.size() == mArity);
+ meta["required"] = array;
+ return meta;
+ }
+};
+
+/**
+ * DispatchEntry subclass for dispatching LLSD::Map to functions accepting
+ * arbitrary argument types (convertible via LLSDParam)
+ */
+struct LLEventDispatcher::MapParamsDispatchEntry: public LLEventDispatcher::ParamsDispatchEntry
+{
+ MapParamsDispatchEntry(const std::string& name, const std::string& desc,
+ const invoker_function& func,
+ const LLSD& params, const LLSD& defaults):
+ ParamsDispatchEntry(desc, func),
+ mMapper(name, params, defaults),
+ mRequired(LLSD::emptyMap())
+ {
+ // Build the set of all param keys, then delete the ones that are
+ // optional. What's left are the ones that are required.
+ for (LLSD::array_const_iterator pi(params.beginArray()), pend(params.endArray());
+ pi != pend; ++pi)
+ {
+ mRequired[pi->asString()] = LLSD();
+ }
+
+ if (defaults.isArray() || defaults.isUndefined())
+ {
+ // Right-align the params and defaults arrays.
+ LLSD::Integer offset = params.size() - defaults.size();
+ // Now the name of every defaults[i] is at params[i + offset].
+ for (LLSD::Integer i(0), iend(defaults.size()); i < iend; ++i)
+ {
+ // Erase this optional param from mRequired.
+ mRequired.erase(params[i + offset].asString());
+ // Instead, make an entry in mOptional with the default
+ // param's name and value.
+ mOptional[params[i + offset].asString()] = defaults[i];
+ }
+ }
+ else if (defaults.isMap())
+ {
+ // if defaults is already a map, then it's already in the form we
+ // intend to deliver in metadata
+ mOptional = defaults;
+ // Just delete from mRequired every key appearing in mOptional.
+ for (LLSD::map_const_iterator mi(mOptional.beginMap()), mend(mOptional.endMap());
+ mi != mend; ++mi)
+ {
+ mRequired.erase(mi->first);
+ }
+ }
+ }
+
+ LLSDArgsMapper mMapper;
+ LLSD mRequired;
+ LLSD mOptional;
+
+ virtual void call(const std::string& desc, const LLSD& event) const
+ {
+ // Just convert from LLSD::Map to LLSD::Array using mMapper, then pass
+ // to base-class call() method.
+ ParamsDispatchEntry::call(desc, mMapper.map(event));
+ }
+
+ virtual LLSD addMetadata(LLSD meta) const
+ {
+ meta["required"] = mRequired;
+ meta["optional"] = mOptional;
+ return meta;
+ }
+};
+
+void LLEventDispatcher::addArrayParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ LLSD::Integer arity)
+{
+ mDispatch.insert(
+ DispatchMap::value_type(name, DispatchMap::mapped_type(
+ new ArrayParamsDispatchEntry(desc, invoker, arity))));
+}
+
+void LLEventDispatcher::addMapParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ const LLSD& params,
+ const LLSD& defaults)
+{
+ mDispatch.insert(
+ DispatchMap::value_type(name, DispatchMap::mapped_type(
+ new MapParamsDispatchEntry(name, desc, invoker, params, defaults))));
+}
+
/// Register a callable by name
void LLEventDispatcher::add(const std::string& name, const std::string& desc,
const Callable& callable, const LLSD& required)
{
- mDispatch.insert(DispatchMap::value_type(name,
- DispatchMap::mapped_type(callable, desc, required)));
+ mDispatch.insert(
+ DispatchMap::value_type(name, DispatchMap::mapped_type(
+ new LLSDDispatchEntry(desc, callable, required))));
}
void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
@@ -66,7 +600,7 @@ bool LLEventDispatcher::remove(const std::string& name)
/// such callable exists, die with LL_ERRS.
void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
{
- if (! attemptCall(name, event))
+ if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
<< "' not found" << LL_ENDL;
@@ -81,44 +615,29 @@ void LLEventDispatcher::operator()(const LLSD& event) const
// This could/should be implemented in terms of the two-arg overload.
// However -- we can produce a more informative error message.
std::string name(event[mKey]);
- if (! attemptCall(name, event))
+ if (! try_call(name, event))
{
LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
<< " value '" << name << "'" << LL_ENDL;
}
}
-bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const
+bool LLEventDispatcher::try_call(const LLSD& event) const
{
- DispatchMap::const_iterator found = mDispatch.find(name);
- if (found == mDispatch.end())
- {
- // The reason we only return false, leaving it up to our caller to die
- // with LL_ERRS, is that different callers have different amounts of
- // available information.
- return false;
- }
- // Found the name, so it's plausible to even attempt the call. But first,
- // validate the syntax of the event itself.
- std::string mismatch(llsd_matches(found->second.mRequired, event));
- if (! mismatch.empty())
- {
- LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name
- << "': bad request: " << mismatch << LL_ENDL;
- }
- // Event syntax looks good, go for it!
- (found->second.mFunc)(event);
- return true; // tell caller we were able to call
+ return try_call(event[mKey], event);
}
-LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const
+bool LLEventDispatcher::try_call(const std::string& name, const LLSD& event) const
{
DispatchMap::const_iterator found = mDispatch.find(name);
if (found == mDispatch.end())
{
- return Callable();
+ return false;
}
- return found->second.mFunc;
+ // Found the name, so it's plausible to even attempt the call.
+ found->second->call(STRINGIZE("LLEventDispatcher(" << mDesc << ") calling '" << name << "'"),
+ event);
+ return true; // tell caller we were able to call
}
LLSD LLEventDispatcher::getMetadata(const std::string& name) const
@@ -130,9 +649,8 @@ LLSD LLEventDispatcher::getMetadata(const std::string& name) const
}
LLSD meta;
meta["name"] = name;
- meta["desc"] = found->second.mDesc;
- meta["required"] = found->second.mRequired;
- return meta;
+ meta["desc"] = found->second->mDesc;
+ return found->second->addMetadata(meta);
}
LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
@@ -147,3 +665,8 @@ bool LLDispatchListener::process(const LLSD& event)
(*this)(event);
return false;
}
+
+LLEventDispatcher::DispatchEntry::DispatchEntry(const std::string& desc):
+ mDesc(desc)
+{}
+
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
index 1e625bcee8..7acc61de4e 100644..100755
--- a/indra/llcommon/lleventdispatcher.h
+++ b/indra/llcommon/lleventdispatcher.h
@@ -7,21 +7,76 @@
* request different operations, vs. instantiating a different
* LLEventPump for each such operation.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
+ *
+ * The invoker machinery that constructs a boost::fusion argument list for use
+ * with boost::fusion::invoke() is derived from
+ * http://www.boost.org/doc/libs/1_45_0/libs/function_types/example/interpreter.hpp
+ * whose license information is copied below:
+ *
+ * "(C) Copyright Tobias Schwinger
+ *
+ * Use modification and distribution are subject to the boost Software License,
+ * Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt)."
*/
#if ! defined(LL_LLEVENTDISPATCHER_H)
#define LL_LLEVENTDISPATCHER_H
+// nil is too generic a term to be allowed to be a global macro. In
+// particular, boost::fusion defines a 'class nil' (properly encapsulated in a
+// namespace) that a global 'nil' macro breaks badly.
+#if defined(nil)
+// Capture the value of the macro 'nil', hoping int is an appropriate type.
+static const int nil_(nil);
+// Now forget the macro.
+#undef nil
+// Finally, reintroduce 'nil' as a properly-scoped alias for the previously-
+// defined const 'nil_'. Make it static since otherwise it produces duplicate-
+// symbol link errors later.
+static const int& nil(nil_);
+#endif
+
#include <string>
-#include <map>
+#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/iterator/transform_iterator.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/function_types/is_nonmember_callable_builtin.hpp>
+#include <boost/function_types/parameter_types.hpp>
+#include <boost/function_types/function_arity.hpp>
+#include <boost/type_traits/remove_cv.hpp>
+#include <boost/type_traits/remove_reference.hpp>
+#include <boost/fusion/include/push_back.hpp>
+#include <boost/fusion/include/cons.hpp>
+#include <boost/fusion/include/invoke.hpp>
+#include <boost/mpl/begin.hpp>
+#include <boost/mpl/end.hpp>
+#include <boost/mpl/next.hpp>
+#include <boost/mpl/deref.hpp>
#include <typeinfo>
#include "llevents.h"
+#include "llsdutil.h"
class LLSD;
@@ -37,12 +92,18 @@ public:
LLEventDispatcher(const std::string& desc, const std::string& key);
virtual ~LLEventDispatcher();
- /// Accept any C++ callable, typically a boost::bind() expression
+ /// @name Register functions accepting(const LLSD&)
+ //@{
+
+ /// Accept any C++ callable with the right signature, typically a
+ /// boost::bind() expression
typedef boost::function<void(const LLSD&)> Callable;
/**
- * Register a @a callable by @a name. The optional @a required parameter
- * is used to validate the structure of each incoming event (see
+ * Register a @a callable by @a name. The passed @a callable accepts a
+ * single LLSD value and uses it in any way desired, e.g. extract
+ * parameters and call some other function. The optional @a required
+ * parameter is used to validate the structure of each incoming event (see
* llsd_matches()).
*/
void add(const std::string& name,
@@ -51,9 +112,23 @@ public:
const LLSD& required=LLSD());
/**
+ * The case of a free function (or static method) accepting(const LLSD&)
+ * could also be intercepted by the arbitrary-args overload below. Ensure
+ * that it's directed to the Callable overload above instead.
+ */
+ void add(const std::string& name,
+ const std::string& desc,
+ void (*f)(const LLSD&),
+ const LLSD& required=LLSD())
+ {
+ add(name, desc, Callable(f), required);
+ }
+
+ /**
* Special case: a subclass of this class can pass an unbound member
- * function pointer without explicitly specifying the
- * <tt>boost::bind()</tt> expression.
+ * function pointer (of an LLEventDispatcher subclass) without explicitly
+ * specifying the <tt>boost::bind()</tt> expression. The passed @a method
+ * accepts a single LLSD value, presumably containing other parameters.
*/
template <class CLASS>
void add(const std::string& name,
@@ -64,7 +139,8 @@ public:
addMethod<CLASS>(name, desc, method, required);
}
- /// Overload for both const and non-const methods
+ /// Overload for both const and non-const methods. The passed @a method
+ /// accepts a single LLSD value, presumably containing other parameters.
template <class CLASS>
void add(const std::string& name,
const std::string& desc,
@@ -74,15 +150,106 @@ public:
addMethod<CLASS>(name, desc, method, required);
}
- /// Convenience: for LLEventDispatcher, not every callable needs a
- /// documentation string.
- template <typename CALLABLE>
- void add(const std::string& name,
- CALLABLE callable,
- const LLSD& required=LLSD())
- {
- add(name, "", callable, required);
- }
+ //@}
+
+ /// @name Register functions with arbitrary param lists
+ //@{
+
+ /**
+ * Register a free function with arbitrary parameters. (This also works
+ * for static class methods.)
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * When calling this name, pass an LLSD::Array. Each entry in turn will be
+ * converted to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Function>
+ typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Function f);
+
+ /**
+ * Register a nonstatic class method with arbitrary parameters.
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * To cover cases such as a method on an LLSingleton we don't yet want to
+ * instantiate, instead of directly storing an instance pointer, accept a
+ * nullary callable returning a pointer/reference to the desired class
+ * instance. If you already have an instance in hand,
+ * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
+ * produce suitable callables.
+ *
+ * When calling this name, pass an LLSD::Array. Each entry in turn will be
+ * converted to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Method, typename InstanceGetter>
+ typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Method f,
+ const InstanceGetter& getter);
+
+ /**
+ * Register a free function with arbitrary parameters. (This also works
+ * for static class methods.)
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * Pass an LLSD::Array of parameter names, and optionally another
+ * LLSD::Array of default parameter values, a la LLSDArgsMapper.
+ *
+ * When calling this name, pass an LLSD::Map. We will internally generate
+ * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
+ * to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Function>
+ typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Function f,
+ const LLSD& params,
+ const LLSD& defaults=LLSD());
+
+ /**
+ * Register a nonstatic class method with arbitrary parameters.
+ *
+ * @note This supports functions with up to about 6 parameters -- after
+ * that you start getting dismaying compile errors in which
+ * boost::fusion::joint_view is mentioned a surprising number of times.
+ *
+ * To cover cases such as a method on an LLSingleton we don't yet want to
+ * instantiate, instead of directly storing an instance pointer, accept a
+ * nullary callable returning a pointer/reference to the desired class
+ * instance. If you already have an instance in hand,
+ * boost::lambda::var(instance) or boost::lambda::constant(instance_ptr)
+ * produce suitable callables.
+ *
+ * Pass an LLSD::Array of parameter names, and optionally another
+ * LLSD::Array of default parameter values, a la LLSDArgsMapper.
+ *
+ * When calling this name, pass an LLSD::Map. We will internally generate
+ * an LLSD::Array using LLSDArgsMapper and then convert each entry in turn
+ * to the corresponding parameter type using LLSDParam.
+ */
+ template<typename Method, typename InstanceGetter>
+ typename boost::enable_if< boost::function_types::is_member_function_pointer<Method>
+ >::type add(const std::string& name,
+ const std::string& desc,
+ Method f,
+ const InstanceGetter& getter,
+ const LLSD& params,
+ const LLSD& defaults=LLSD());
+
+ //@}
/// Unregister a callable
bool remove(const std::string& name);
@@ -92,12 +259,25 @@ public:
/// the @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const std::string& name, const LLSD& event) const;
+ /// Call a registered callable with an explicitly-specified name and
+ /// return <tt>true</tt>. If no such callable exists, return
+ /// <tt>false</tt>. If the @a event fails to match the @a required
+ /// prototype specified at add() time, die with LL_ERRS.
+ bool try_call(const std::string& name, const LLSD& event) const;
+
/// Extract the @a key value from the incoming @a event, and call the
/// callable whose name is specified by that map @a key. If no such
/// callable exists, die with LL_ERRS. If the @a event fails to match the
/// @a required prototype specified at add() time, die with LL_ERRS.
void operator()(const LLSD& event) const;
+ /// Extract the @a key value from the incoming @a event, call the callable
+ /// whose name is specified by that map @a key and return <tt>true</tt>.
+ /// If no such callable exists, return <tt>false</tt>. If the @a event
+ /// fails to match the @a required prototype specified at add() time, die
+ /// with LL_ERRS.
+ bool try_call(const LLSD& event) const;
+
/// @name Iterate over defined names
//@{
typedef std::pair<std::string, std::string> NameDesc;
@@ -105,16 +285,22 @@ public:
private:
struct DispatchEntry
{
- DispatchEntry(const Callable& func, const std::string& desc, const LLSD& required):
- mFunc(func),
- mDesc(desc),
- mRequired(required)
- {}
- Callable mFunc;
+ DispatchEntry(const std::string& desc);
+ virtual ~DispatchEntry() {} // suppress MSVC warning, sigh
+
std::string mDesc;
- LLSD mRequired;
+
+ virtual void call(const std::string& desc, const LLSD& event) const = 0;
+ virtual LLSD addMetadata(LLSD) const = 0;
};
- typedef std::map<std::string, DispatchEntry> DispatchMap;
+ // Tried using boost::ptr_map<std::string, DispatchEntry>, but ptr_map<>
+ // wants its value type to be "clonable," even just to dereference an
+ // iterator. I don't want to clone entries -- if I have to copy an entry
+ // around, I want it to continue pointing to the same DispatchEntry
+ // subclass object. However, I definitely want DispatchMap to destroy
+ // DispatchEntry if no references are outstanding at the time an entry is
+ // removed. This looks like a job for boost::shared_ptr.
+ typedef std::map<std::string, boost::shared_ptr<DispatchEntry> > DispatchMap;
public:
/// We want the flexibility to redefine what data we store per name,
@@ -132,10 +318,6 @@ public:
}
//@}
- /// Fetch the Callable for the specified name. If no such name was
- /// registered, return an empty() Callable.
- Callable get(const std::string& name) const;
-
/// Get information about a specific Callable
LLSD getMetadata(const std::string& name) const;
@@ -158,18 +340,184 @@ private:
}
}
void addFail(const std::string& name, const std::string& classname) const;
- /// try to dispatch, return @c true if success
- bool attemptCall(const std::string& name, const LLSD& event) const;
std::string mDesc, mKey;
DispatchMap mDispatch;
static NameDesc makeNameDesc(const DispatchMap::value_type& item)
{
- return NameDesc(item.first, item.second.mDesc);
+ return NameDesc(item.first, item.second->mDesc);
+ }
+
+ struct LLSDDispatchEntry;
+ struct ParamsDispatchEntry;
+ struct ArrayParamsDispatchEntry;
+ struct MapParamsDispatchEntry;
+
+ // Step 2 of parameter analysis. Instantiating invoker<some_function_type>
+ // implicitly sets its From and To parameters to the (compile time) begin
+ // and end iterators over that function's parameter types.
+ template< typename Function
+ , class From = typename boost::mpl::begin< boost::function_types::parameter_types<Function> >::type
+ , class To = typename boost::mpl::end< boost::function_types::parameter_types<Function> >::type
+ >
+ struct invoker;
+
+ // deliver LLSD arguments one at a time
+ typedef boost::function<LLSD()> args_source;
+ // obtain args from an args_source to build param list and call target
+ // function
+ typedef boost::function<void(const args_source&)> invoker_function;
+
+ template <typename Function>
+ invoker_function make_invoker(Function f);
+ template <typename Method, typename InstanceGetter>
+ invoker_function make_invoker(Method f, const InstanceGetter& getter);
+ void addArrayParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ LLSD::Integer arity);
+ void addMapParamsDispatchEntry(const std::string& name,
+ const std::string& desc,
+ const invoker_function& invoker,
+ const LLSD& params,
+ const LLSD& defaults);
+};
+
+/*****************************************************************************
+* LLEventDispatcher template implementation details
+*****************************************************************************/
+// Step 3 of parameter analysis, the recursive case.
+template<typename Function, class From, class To>
+struct LLEventDispatcher::invoker
+{
+ template<typename T>
+ struct remove_cv_ref
+ : boost::remove_cv< typename boost::remove_reference<T>::type >
+ { };
+
+ // apply() accepts an arbitrary boost::fusion sequence as args. It
+ // examines the next parameter type in the parameter-types sequence
+ // bounded by From and To, obtains the next LLSD object from the passed
+ // args_source and constructs an LLSDParam of appropriate type to try
+ // to convert the value. It then recurs with the next parameter-types
+ // iterator, passing the args sequence thus far.
+ template<typename Args>
+ static inline
+ void apply(Function func, const args_source& argsrc, Args const & args)
+ {
+ typedef typename boost::mpl::deref<From>::type arg_type;
+ typedef typename boost::mpl::next<From>::type next_iter_type;
+ typedef typename remove_cv_ref<arg_type>::type plain_arg_type;
+
+ invoker<Function, next_iter_type, To>::apply
+ ( func, argsrc, boost::fusion::push_back(args, LLSDParam<plain_arg_type>(argsrc())));
+ }
+
+ // Special treatment for instance (first) parameter of a non-static member
+ // function. Accept the instance-getter callable, calling that to produce
+ // the first args value. Since we know we're at the top of the recursion
+ // chain, we need not also require a partial args sequence from our caller.
+ template <typename InstanceGetter>
+ static inline
+ void method_apply(Function func, const args_source& argsrc, const InstanceGetter& getter)
+ {
+ typedef typename boost::mpl::next<From>::type next_iter_type;
+
+ // Instead of grabbing the first item from argsrc and making an
+ // LLSDParam of it, call getter() and pass that as the instance param.
+ invoker<Function, next_iter_type, To>::apply
+ ( func, argsrc, boost::fusion::push_back(boost::fusion::nil(), boost::ref(getter())));
}
};
+// Step 4 of parameter analysis, the leaf case. When the general
+// invoker<Function, From, To> logic has advanced From until it matches To,
+// the compiler will pick this template specialization.
+template<typename Function, class To>
+struct LLEventDispatcher::invoker<Function,To,To>
+{
+ // the argument list is complete, now call the function
+ template<typename Args>
+ static inline
+ void apply(Function func, const args_source&, Args const & args)
+ {
+ boost::fusion::invoke(func, args);
+ }
+};
+
+template<typename Function>
+typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f)
+{
+ // Construct an invoker_function, a callable accepting const args_source&.
+ // Add to DispatchMap an ArrayParamsDispatchEntry that will handle the
+ // caller's LLSD::Array.
+ addArrayParamsDispatchEntry(name, desc, make_invoker(f),
+ boost::function_types::function_arity<Function>::value);
+}
+
+template<typename Method, typename InstanceGetter>
+typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
+ const InstanceGetter& getter)
+{
+ // Subtract 1 from the compile-time arity because the getter takes care of
+ // the first parameter. We only need (arity - 1) additional arguments.
+ addArrayParamsDispatchEntry(name, desc, make_invoker(f, getter),
+ boost::function_types::function_arity<Method>::value - 1);
+}
+
+template<typename Function>
+typename boost::enable_if< boost::function_types::is_nonmember_callable_builtin<Function> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Function f,
+ const LLSD& params, const LLSD& defaults)
+{
+ // See comments for previous is_nonmember_callable_builtin add().
+ addMapParamsDispatchEntry(name, desc, make_invoker(f), params, defaults);
+}
+
+template<typename Method, typename InstanceGetter>
+typename boost::enable_if< boost::function_types::is_member_function_pointer<Method> >::type
+LLEventDispatcher::add(const std::string& name, const std::string& desc, Method f,
+ const InstanceGetter& getter,
+ const LLSD& params, const LLSD& defaults)
+{
+ addMapParamsDispatchEntry(name, desc, make_invoker(f, getter), params, defaults);
+}
+
+template <typename Function>
+LLEventDispatcher::invoker_function
+LLEventDispatcher::make_invoker(Function f)
+{
+ // Step 1 of parameter analysis, the top of the recursion. Passing a
+ // suitable f (see add()'s enable_if condition) to this method causes it
+ // to infer the function type; specifying that function type to invoker<>
+ // causes it to fill in the begin/end MPL iterators over the function's
+ // list of parameter types.
+ // While normally invoker::apply() could infer its template type from the
+ // boost::fusion::nil parameter value, here we must be explicit since
+ // we're boost::bind()ing it rather than calling it directly.
+ return boost::bind(&invoker<Function>::template apply<boost::fusion::nil>,
+ f,
+ _1,
+ boost::fusion::nil());
+}
+
+template <typename Method, typename InstanceGetter>
+LLEventDispatcher::invoker_function
+LLEventDispatcher::make_invoker(Method f, const InstanceGetter& getter)
+{
+ // Use invoker::method_apply() to treat the instance (first) arg specially.
+ return boost::bind(&invoker<Method>::template method_apply<InstanceGetter>,
+ f,
+ _1,
+ getter);
+}
+
+/*****************************************************************************
+* LLDispatchListener
+*****************************************************************************/
/**
* Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
* that contains (or derives from) LLDispatchListener need only specify the
diff --git a/indra/llcommon/lleventemitter.h b/indra/llcommon/lleventemitter.h
index 2d15b0145a..cd82fc56f9 100644..100755
--- a/indra/llcommon/lleventemitter.h
+++ b/indra/llcommon/lleventemitter.h
@@ -2,31 +2,25 @@
* @file lleventemitter.h
* @brief General event emitter class
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp
index 74133781be..d36a107254 100644..100755
--- a/indra/llcommon/lleventfilter.cpp
+++ b/indra/llcommon/lleventfilter.cpp
@@ -4,8 +4,25 @@
* @date 2009-03-05
* @brief Implementation for lleventfilter.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h
index 89f0c7ea43..e822a664f5 100644..100755
--- a/indra/llcommon/lleventfilter.h
+++ b/indra/llcommon/lleventfilter.h
@@ -4,8 +4,25 @@
* @date 2009-03-05
* @brief Define LLEventFilter: LLEventStream subclass with conditions
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 31fdd9e60a..1c928b3db8 100644..100755
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -4,8 +4,25 @@
* @date 2008-09-12
* @brief Implementation for llevents.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -24,7 +41,6 @@
#include <algorithm>
// std headers
#include <typeinfo>
-#include <cassert>
#include <cmath>
#include <cctype>
// external library headers
@@ -413,13 +429,13 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
{
// The new node isn't last. Place it between the previous node and
// the successor.
- newNode = (myprev + mydmi->second)/2.0;
+ newNode = (myprev + mydmi->second)/2.f;
}
else
{
// The new node is last. Bump myprev up to the next integer, add
// 1.0 and use that.
- newNode = std::ceil(myprev) + 1.0;
+ newNode = std::ceil(myprev) + 1.f;
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
@@ -458,7 +474,7 @@ void LLEventPump::stopListening(const std::string& name)
*****************************************************************************/
bool LLEventStream::post(const LLSD& event)
{
- if (! mEnabled)
+ if (! mEnabled || !mSignal)
{
return false;
}
@@ -498,6 +514,8 @@ bool LLEventQueue::post(const LLSD& event)
void LLEventQueue::flush()
{
+ if(!mSignal) return;
+
// Consider the case when a given listener on this LLEventQueue posts yet
// another event on the same queue. If we loop over mEventQueue directly,
// we'll end up processing all those events during the same flush() call
@@ -569,3 +587,27 @@ void LLReqID::stamp(LLSD& response) const
}
response["reqid"] = mReqid;
}
+
+bool sendReply(const LLSD& reply, const LLSD& request, const std::string& replyKey)
+{
+ // If the original request has no value for replyKey, it's pointless to
+ // construct or send a reply event: on which LLEventPump should we send
+ // it? Allow that to be optional: if the caller wants to require replyKey,
+ // it can so specify when registering the operation method.
+ if (! request.has(replyKey))
+ {
+ return false;
+ }
+
+ // Here the request definitely contains replyKey; reasonable to proceed.
+
+ // Copy 'reply' to modify it.
+ LLSD newreply(reply);
+ // Get the ["reqid"] element from request
+ LLReqID reqID(request);
+ // and copy it to 'newreply'.
+ reqID.stamp(newreply);
+ // Send reply on LLEventPump named in request[replyKey]. Don't forget to
+ // send the modified 'newreply' instead of the original 'reply'.
+ return LLEventPumps::instance().obtain(request[replyKey]).post(newreply);
+}
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index a73ada2931..0cbd1da32d 100644..100755
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -7,8 +7,25 @@
* originally introduced in llnotifications.h. It has nothing
* whatsoever to do with the older system in llevent.h.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,7 +61,7 @@
#include "llsd.h"
#include "llsingleton.h"
#include "lldependencies.h"
-#include "ll_template_cast.h"
+#include "llstl.h"
/*==========================================================================*|
// override this to allow binding free functions with more parameters
@@ -675,6 +692,20 @@ private:
};
/**
+ * Conventionally send a reply to a request event.
+ *
+ * @a reply is the LLSD reply event to send
+ * @a request is the corresponding LLSD request event
+ * @a replyKey is the key in the @a request event, conventionally ["reply"],
+ * whose value is the name of the LLEventPump on which to send the reply.
+ *
+ * Before sending the reply event, sendReply() copies the ["reqid"] item from
+ * the request to the reply.
+ */
+LL_COMMON_API bool sendReply(const LLSD& reply, const LLSD& request,
+ const std::string& replyKey="reply");
+
+/**
* Base class for LLListenerWrapper. See visit_and_connect() and llwrap(). We
* provide virtual @c accept_xxx() methods, customization points allowing a
* subclass access to certain data visible at LLEventPump::listen() time.
diff --git a/indra/llcommon/lleventtimer.cpp b/indra/llcommon/lleventtimer.cpp
index d44e7ec1e6..0d96e03da4 100644..100755
--- a/indra/llcommon/lleventtimer.cpp
+++ b/indra/llcommon/lleventtimer.cpp
@@ -2,31 +2,25 @@
* @file lleventtimer.cpp
* @brief Cross-platform objects for doing timing
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -64,19 +58,15 @@ LLEventTimer::~LLEventTimer()
void LLEventTimer::updateClass()
{
std::list<LLEventTimer*> completed_timers;
-
+ for (instance_iter iter = beginInstances(); iter != endInstances(); )
{
- LLInstanceTrackerScopedGuard guard;
- for (instance_iter iter = guard.beginInstances(); iter != guard.endInstances(); )
- {
- LLEventTimer& timer = *iter++;
- F32 et = timer.mEventTimer.getElapsedTimeF32();
- if (timer.mEventTimer.getStarted() && et > timer.mPeriod) {
- timer.mEventTimer.reset();
- if ( timer.tick() )
- {
- completed_timers.push_back( &timer );
- }
+ LLEventTimer& timer = *iter++;
+ F32 et = timer.mEventTimer.getElapsedTimeF32();
+ if (timer.mEventTimer.getStarted() && et > timer.mPeriod) {
+ timer.mEventTimer.reset();
+ if ( timer.tick() )
+ {
+ completed_timers.push_back( &timer );
}
}
}
diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h
index 5181cce52d..dc918121e1 100644..100755
--- a/indra/llcommon/lleventtimer.h
+++ b/indra/llcommon/lleventtimer.h
@@ -2,31 +2,25 @@
* @file lleventtimer.h
* @brief Cross-platform objects for doing timing
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,9 +33,10 @@
#include "lltimer.h"
// class for scheduling a function to be called at a given frequency (approximate, inprecise)
-class LL_COMMON_API LLEventTimer : protected LLInstanceTracker<LLEventTimer>
+class LL_COMMON_API LLEventTimer : public LLInstanceTracker<LLEventTimer>
{
public:
+
LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds
LLEventTimer(const LLDate& time);
virtual ~LLEventTimer();
diff --git a/indra/llcommon/llextendedstatus.h b/indra/llcommon/llextendedstatus.h
deleted file mode 100644
index 4f954ff17a..0000000000
--- a/indra/llcommon/llextendedstatus.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/**
- * @file llextendedstatus.h
- * @date August 2007
- * @brief extended status codes for curl/vfs/resident asset storage and delivery
- *
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLEXTENDEDSTATUS_H
-#define LL_LLEXTENDEDSTATUS_H
-
-
-typedef S32 LLExtStat;
-
-
-// Status provider groups - Top bits indicate which status type it is
-// Zero is common status code (next section)
-const LLExtStat LL_EXSTAT_CURL_RESULT = 1L<<30; // serviced by curl - use 1L if we really implement the below
-const LLExtStat LL_EXSTAT_RES_RESULT = 2L<<30; // serviced by resident copy
-const LLExtStat LL_EXSTAT_VFS_RESULT = 3L<<30; // serviced by vfs
-
-
-// Common Status Codes
-//
-const LLExtStat LL_EXSTAT_NONE = 0x00000; // No extra info here - sorry!
-const LLExtStat LL_EXSTAT_NULL_UUID = 0x10001; // null asset ID
-const LLExtStat LL_EXSTAT_NO_UPSTREAM = 0x10002; // attempt to upload without a valid upstream method/provider
-const LLExtStat LL_EXSTAT_REQUEST_DROPPED = 0x10003; // request was dropped unserviced
-const LLExtStat LL_EXSTAT_NONEXISTENT_FILE = 0x10004; // trying to upload a file that doesn't exist
-const LLExtStat LL_EXSTAT_BLOCKED_FILE = 0x10005; // trying to upload a file that we can't open
-
-
-// curl status codes:
-//
-// Mask off LL_EXSTAT_CURL_RESULT for original result and
-// see: libraries/include/curl/curl.h
-
-
-// Memory-Resident status codes:
-// None at present
-
-
-// VFS status codes:
-const LLExtStat LL_EXSTAT_VFS_CACHED = LL_EXSTAT_VFS_RESULT | 0x0001;
-const LLExtStat LL_EXSTAT_VFS_CORRUPT = LL_EXSTAT_VFS_RESULT | 0x0002;
-
-
-#endif // LL_LLEXTENDEDSTATUS_H
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
new file mode 100755
index 0000000000..3d28cd15b0
--- /dev/null
+++ b/indra/llcommon/llfasttimer.cpp
@@ -0,0 +1,491 @@
+/**
+ * @file llfasttimer.cpp
+ * @brief Implementation of the fast timer.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llfasttimer.h"
+
+#include "llmemory.h"
+#include "llprocessor.h"
+#include "llsingleton.h"
+#include "lltreeiterators.h"
+#include "llsdserialize.h"
+#include "llunits.h"
+#include "llsd.h"
+#include "lltracerecording.h"
+#include "lltracethreadrecorder.h"
+
+#include <boost/bind.hpp>
+#include <queue>
+
+
+#if LL_WINDOWS
+#include "lltimer.h"
+#elif LL_LINUX || LL_SOLARIS
+#include <sys/time.h>
+#include <sched.h>
+#include "lltimer.h"
+#elif LL_DARWIN
+#include <sys/time.h>
+#include "lltimer.h" // get_clock_count()
+#else
+#error "architecture not supported"
+#endif
+
+namespace LLTrace
+{
+
+//////////////////////////////////////////////////////////////////////////////
+// statics
+
+bool BlockTimer::sLog = false;
+std::string BlockTimer::sLogName = "";
+bool BlockTimer::sMetricLog = false;
+
+#if LL_LINUX || LL_SOLARIS
+U64 BlockTimer::sClockResolution = 1000000000; // Nanosecond resolution
+#else
+U64 BlockTimer::sClockResolution = 1000000; // Microsecond resolution
+#endif
+
+static LLMutex* sLogLock = NULL;
+static std::queue<LLSD> sLogQueue;
+
+block_timer_tree_df_iterator_t begin_block_timer_tree_df(BlockTimerStatHandle& id)
+{
+ return block_timer_tree_df_iterator_t(&id,
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1),
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1));
+}
+
+block_timer_tree_df_iterator_t end_block_timer_tree_df()
+{
+ return block_timer_tree_df_iterator_t();
+}
+
+block_timer_tree_df_post_iterator_t begin_block_timer_tree_df_post(BlockTimerStatHandle& id)
+{
+ return block_timer_tree_df_post_iterator_t(&id,
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1),
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1));
+}
+
+block_timer_tree_df_post_iterator_t end_block_timer_tree_df_post()
+{
+ return block_timer_tree_df_post_iterator_t();
+}
+
+block_timer_tree_bf_iterator_t begin_block_timer_tree_bf(BlockTimerStatHandle& id)
+{
+ return block_timer_tree_bf_iterator_t(&id,
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1),
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1));
+}
+
+block_timer_tree_bf_iterator_t end_block_timer_tree_bf()
+ {
+ return block_timer_tree_bf_iterator_t();
+ }
+
+block_timer_tree_df_iterator_t begin_timer_tree(BlockTimerStatHandle& id)
+ {
+ return block_timer_tree_df_iterator_t(&id,
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1),
+ boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1));
+ }
+
+block_timer_tree_df_iterator_t end_timer_tree()
+ {
+ return block_timer_tree_df_iterator_t();
+}
+
+
+// sort child timers by name
+struct SortTimerByName
+ {
+ bool operator()(const BlockTimerStatHandle* i1, const BlockTimerStatHandle* i2)
+ {
+ return i1->getName() < i2->getName();
+ }
+};
+
+static BlockTimerStatHandle sRootTimer("root", NULL);
+BlockTimerStatHandle& BlockTimer::getRootTimeBlock()
+{
+ return sRootTimer;
+ }
+
+void BlockTimer::pushLog(LLSD log)
+{
+ LLMutexLock lock(sLogLock);
+
+ sLogQueue.push(log);
+}
+
+void BlockTimer::setLogLock(LLMutex* lock)
+{
+ sLogLock = lock;
+}
+
+
+//static
+#if (LL_DARWIN || LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
+U64 BlockTimer::countsPerSecond()
+{
+ return sClockResolution;
+}
+#else // windows or x86-mac or x86-linux or x86-solaris
+U64 BlockTimer::countsPerSecond()
+{
+#if LL_FASTTIMER_USE_RDTSC || !LL_WINDOWS
+ //getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz
+ static LLUnit<U64, LLUnits::Hertz> sCPUClockFrequency = LLProcessorInfo().getCPUFrequency();
+ return sCPUClockFrequency.value();
+#else
+ // If we're not using RDTSC, each fasttimer tick is just a performance counter tick.
+ // Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency())
+ // since that would change displayed MHz stats for CPUs
+ static bool firstcall = true;
+ static U64 sCPUClockFrequency;
+ if (firstcall)
+ {
+ QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency);
+ firstcall = false;
+ }
+ return sCPUClockFrequency.value();
+#endif
+}
+#endif
+
+BlockTimerStatHandle::BlockTimerStatHandle(const char* name, const char* description)
+: StatType<TimeBlockAccumulator>(name, description)
+{}
+
+TimeBlockTreeNode& BlockTimerStatHandle::getTreeNode() const
+{
+ TimeBlockTreeNode* nodep = LLTrace::get_thread_recorder()->getTimeBlockTreeNode(getIndex());
+ llassert(nodep);
+ return *nodep;
+}
+
+
+void BlockTimer::bootstrapTimerTree()
+{
+ for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(), end_it = BlockTimerStatHandle::instance_tracker_t::endInstances();
+ it != end_it;
+ ++it)
+ {
+ BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(*it);
+ if (&timer == &BlockTimer::getRootTimeBlock()) continue;
+
+ // bootstrap tree construction by attaching to last timer to be on stack
+ // when this timer was called
+ if (timer.getParent() == &BlockTimer::getRootTimeBlock())
+{
+ TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator();
+
+ if (accumulator.mLastCaller)
+ {
+ timer.setParent(accumulator.mLastCaller);
+ accumulator.mParent = accumulator.mLastCaller;
+ }
+ // no need to push up tree on first use, flag can be set spuriously
+ accumulator.mMoveUpTree = false;
+ }
+ }
+}
+
+// bump timers up tree if they have been flagged as being in the wrong place
+// do this in a bottom up order to promote descendants first before promoting ancestors
+// this preserves partial order derived from current frame's observations
+void BlockTimer::incrementalUpdateTimerTree()
+{
+ for(block_timer_tree_df_post_iterator_t it = begin_block_timer_tree_df_post(BlockTimer::getRootTimeBlock());
+ it != end_block_timer_tree_df_post();
+ ++it)
+ {
+ BlockTimerStatHandle* timerp = *it;
+
+ // sort timers by time last called, so call graph makes sense
+ TimeBlockTreeNode& tree_node = timerp->getTreeNode();
+ if (tree_node.mNeedsSorting)
+{
+ std::sort(tree_node.mChildren.begin(), tree_node.mChildren.end(), SortTimerByName());
+}
+
+ // skip root timer
+ if (timerp != &BlockTimer::getRootTimeBlock())
+{
+ TimeBlockAccumulator& accumulator = timerp->getCurrentAccumulator();
+
+ if (accumulator.mMoveUpTree)
+{
+ // since ancestors have already been visited, re-parenting won't affect tree traversal
+ //step up tree, bringing our descendants with us
+ LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
+ " to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL;
+ timerp->setParent(timerp->getParent()->getParent());
+ accumulator.mParent = timerp->getParent();
+ accumulator.mMoveUpTree = false;
+
+ // don't bubble up any ancestors until descendants are done bubbling up
+ // as ancestors may call this timer only on certain paths, so we want to resolve
+ // child-most block locations before their parents
+ it.skipAncestors();
+ }
+ }
+ }
+}
+
+
+void BlockTimer::updateTimes()
+ {
+ // walk up stack of active timers and accumulate current time while leaving timing structures active
+ BlockTimerStackRecord* stack_record = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
+ if (!stack_record) return;
+
+ U64 cur_time = getCPUClockCount64();
+ BlockTimer* cur_timer = stack_record->mActiveTimer;
+ TimeBlockAccumulator* accumulator = &stack_record->mTimeBlock->getCurrentAccumulator();
+
+ while(cur_timer
+ && cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self
+ {
+ U64 cumulative_time_delta = cur_time - cur_timer->mStartTime;
+ cur_timer->mStartTime = cur_time;
+
+ accumulator->mTotalTimeCounter += cumulative_time_delta;
+ accumulator->mSelfTimeCounter += cumulative_time_delta - stack_record->mChildTime;
+ stack_record->mChildTime = 0;
+
+ stack_record = &cur_timer->mParentTimerData;
+ accumulator = &stack_record->mTimeBlock->getCurrentAccumulator();
+ cur_timer = stack_record->mActiveTimer;
+
+ stack_record->mChildTime += cumulative_time_delta;
+ }
+}
+
+static LLTrace::BlockTimerStatHandle FTM_PROCESS_TIMES("Process FastTimer Times");
+
+// not thread safe, so only call on main thread
+//static
+void BlockTimer::processTimes()
+{
+#if LL_TRACE_ENABLED
+ LL_RECORD_BLOCK_TIME(FTM_PROCESS_TIMES);
+ get_clock_count(); // good place to calculate clock frequency
+
+ // set up initial tree
+ bootstrapTimerTree();
+
+ incrementalUpdateTimerTree();
+
+ updateTimes();
+
+ // reset for next frame
+ for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(),
+ end_it = BlockTimerStatHandle::instance_tracker_t::endInstances();
+ it != end_it;
+ ++it)
+ {
+ BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(*it);
+ TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator();
+
+ accumulator.mLastCaller = NULL;
+ accumulator.mMoveUpTree = false;
+ }
+#endif
+}
+
+std::vector<BlockTimerStatHandle*>::iterator BlockTimerStatHandle::beginChildren()
+ {
+ return getTreeNode().mChildren.begin();
+ }
+
+std::vector<BlockTimerStatHandle*>::iterator BlockTimerStatHandle::endChildren()
+ {
+ return getTreeNode().mChildren.end();
+}
+
+std::vector<BlockTimerStatHandle*>& BlockTimerStatHandle::getChildren()
+{
+ return getTreeNode().mChildren;
+ }
+
+bool BlockTimerStatHandle::hasChildren()
+{
+ return ! getTreeNode().mChildren.empty();
+}
+
+// static
+void BlockTimer::logStats()
+{
+ // get ready for next frame
+ if (sLog)
+ { //output current frame counts to performance log
+
+ static S32 call_count = 0;
+ if (call_count % 100 == 0)
+ {
+ LL_DEBUGS("FastTimers") << "countsPerSecond: " << countsPerSecond() << LL_ENDL;
+ LL_DEBUGS("FastTimers") << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << LL_ENDL;
+ LL_DEBUGS("FastTimers") << "getCPUClockCount32() " << getCPUClockCount32() << LL_ENDL;
+ LL_DEBUGS("FastTimers") << "getCPUClockCount64() " << getCPUClockCount64() << LL_ENDL;
+ LL_DEBUGS("FastTimers") << "elapsed sec " << ((F64)getCPUClockCount64() / (F64HertzImplicit)LLProcessorInfo().getCPUFrequency()) << LL_ENDL;
+ }
+ call_count++;
+
+ F64Seconds total_time(0);
+ LLSD sd;
+
+ {
+ for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(),
+ end_it = BlockTimerStatHandle::instance_tracker_t::endInstances();
+ it != end_it;
+ ++it)
+ {
+ BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(*it);
+ LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording();
+ sd[timer.getName()]["Time"] = (LLSD::Real) (frame_recording.getLastRecording().getSum(timer).value());
+ sd[timer.getName()]["Calls"] = (LLSD::Integer) (frame_recording.getLastRecording().getSum(timer.callCount()));
+
+ // computing total time here because getting the root timer's getCountHistory
+ // doesn't work correctly on the first frame
+ total_time += frame_recording.getLastRecording().getSum(timer);
+ }
+ }
+
+ sd["Total"]["Time"] = (LLSD::Real) total_time.value();
+ sd["Total"]["Calls"] = (LLSD::Integer) 1;
+
+ {
+ LLMutexLock lock(sLogLock);
+ sLogQueue.push(sd);
+ }
+ }
+
+}
+
+//static
+void BlockTimer::dumpCurTimes()
+{
+ LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording();
+ LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording();
+
+ // walk over timers in depth order and output timings
+ for(block_timer_tree_df_iterator_t it = begin_timer_tree(BlockTimer::getRootTimeBlock());
+ it != end_timer_tree();
+ ++it)
+ {
+ BlockTimerStatHandle* timerp = (*it);
+ F64Seconds total_time = last_frame_recording.getSum(*timerp);
+ U32 num_calls = last_frame_recording.getSum(timerp->callCount());
+
+ // Don't bother with really brief times, keep output concise
+ if (total_time < F32Milliseconds(0.1f)) continue;
+
+ std::ostringstream out_str;
+ BlockTimerStatHandle* parent_timerp = timerp;
+ while(parent_timerp && parent_timerp != parent_timerp->getParent())
+ {
+ out_str << "\t";
+ parent_timerp = parent_timerp->getParent();
+ }
+
+ out_str << timerp->getName() << " "
+ << std::setprecision(3) << total_time.valueInUnits<LLUnits::Milliseconds>() << " ms, "
+ << num_calls << " calls";
+
+ LL_INFOS() << out_str.str() << LL_ENDL;
+}
+}
+
+//static
+void BlockTimer::writeLog(std::ostream& os)
+{
+ while (!sLogQueue.empty())
+ {
+ LLSD& sd = sLogQueue.front();
+ LLSDSerialize::toXML(sd, os);
+ LLMutexLock lock(sLogLock);
+ sLogQueue.pop();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// TimeBlockAccumulator
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+TimeBlockAccumulator::TimeBlockAccumulator()
+: mTotalTimeCounter(0),
+ mSelfTimeCounter(0),
+ mCalls(0),
+ mLastCaller(NULL),
+ mActiveCount(0),
+ mMoveUpTree(false),
+ mParent(NULL)
+{}
+
+void TimeBlockAccumulator::addSamples( const TimeBlockAccumulator& other, EBufferAppendType append_type )
+{
+#if LL_TRACE_ENABLED
+ // we can't merge two unrelated time block samples, as that will screw with the nested timings
+ // due to the call hierarchy of each thread
+ llassert(append_type == SEQUENTIAL);
+ mTotalTimeCounter += other.mTotalTimeCounter;
+ mSelfTimeCounter += other.mSelfTimeCounter;
+ mCalls += other.mCalls;
+ mLastCaller = other.mLastCaller;
+ mActiveCount = other.mActiveCount;
+ mMoveUpTree = other.mMoveUpTree;
+ mParent = other.mParent;
+#endif
+}
+
+void TimeBlockAccumulator::reset( const TimeBlockAccumulator* other )
+{
+ mCalls = 0;
+ mSelfTimeCounter = 0;
+ mTotalTimeCounter = 0;
+
+ if (other)
+{
+ mLastCaller = other->mLastCaller;
+ mActiveCount = other->mActiveCount;
+ mMoveUpTree = other->mMoveUpTree;
+ mParent = other->mParent;
+ }
+}
+
+F64Seconds BlockTimer::getElapsedTime()
+{
+ U64 total_time = getCPUClockCount64() - mStartTime;
+
+ return F64Seconds((F64)total_time / (F64)BlockTimer::countsPerSecond());
+}
+
+
+} // namespace LLTrace
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index 48461df6ae..2370253078 100644..100755
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -1,166 +1,346 @@
/**
* @file llfasttimer.h
- * @brief Inline implementations of fast timers.
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
+ * @brief Declaration of a fast timer.
*
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_FASTTIMER_H
#define LL_FASTTIMER_H
-// pull in the actual class definition
-#include "llfasttimer_class.h"
+#include "llinstancetracker.h"
+#include "lltrace.h"
+#include "lltreeiterators.h"
-#if LL_WINDOWS
-//
-// Windows implementation of CPU clock
-//
-
-//
-// NOTE: put back in when we aren't using platform sdk anymore
-//
-// because MS has different signatures for these functions in winnt.h
-// need to rename them to avoid conflicts
-//#define _interlockedbittestandset _renamed_interlockedbittestandset
-//#define _interlockedbittestandreset _renamed_interlockedbittestandreset
-//#include <intrin.h>
-//#undef _interlockedbittestandset
-//#undef _interlockedbittestandreset
-
-//inline U32 LLFastTimer::getCPUClockCount32()
-//{
-// U64 time_stamp = __rdtsc();
-// return (U32)(time_stamp >> 8);
-//}
-//
-//// return full timer value, *not* shifted by 8 bits
-//inline U64 LLFastTimer::getCPUClockCount64()
-//{
-// return __rdtsc();
-//}
-
-// shift off lower 8 bits for lower resolution but longer term timing
-// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
-inline U32 LLFastTimer::getCPUClockCount32()
+#define LL_FAST_TIMER_ON 1
+#define LL_FASTTIMER_USE_RDTSC 1
+
+#define LL_RECORD_BLOCK_TIME(timer_stat) const LLTrace::BlockTimer& LL_GLUE_TOKENS(block_time_recorder, __LINE__)(LLTrace::timeThisBlock(timer_stat)); (void)LL_GLUE_TOKENS(block_time_recorder, __LINE__);
+
+namespace LLTrace
{
- U32 ret_val;
- __asm
+// use to create blocktimer rvalue to be captured in a reference so that the BlockTimer lives to the end of the block.
+class BlockTimer timeThisBlock(class BlockTimerStatHandle& timer);
+
+class BlockTimer
+{
+public:
+ typedef BlockTimer self_t;
+ typedef class BlockTimerStatHandle DeclareTimer;
+
+ ~BlockTimer();
+
+ F64Seconds getElapsedTime();
+
+ //////////////////////////////////////////////////////////////////////////////
+ //
+ // Important note: These implementations must be FAST!
+ //
+
+
+#if LL_WINDOWS
+ //
+ // Windows implementation of CPU clock
+ //
+
+ //
+ // NOTE: put back in when we aren't using platform sdk anymore
+ //
+ // because MS has different signatures for these functions in winnt.h
+ // need to rename them to avoid conflicts
+ //#define _interlockedbittestandset _renamed_interlockedbittestandset
+ //#define _interlockedbittestandreset _renamed_interlockedbittestandreset
+ //#include <intrin.h>
+ //#undef _interlockedbittestandset
+ //#undef _interlockedbittestandreset
+
+ //inline U32 getCPUClockCount32()
+ //{
+ // U64 time_stamp = __rdtsc();
+ // return (U32)(time_stamp >> 8);
+ //}
+ //
+ //// return full timer value, *not* shifted by 8 bits
+ //inline U64 getCPUClockCount64()
+ //{
+ // return __rdtsc();
+ //}
+
+ // shift off lower 8 bits for lower resolution but longer term timing
+ // on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
+#if LL_FASTTIMER_USE_RDTSC
+ static U32 getCPUClockCount32()
{
- _emit 0x0f
- _emit 0x31
- shr eax,8
- shl edx,24
- or eax, edx
- mov dword ptr [ret_val], eax
+ U32 ret_val;
+ __asm
+ {
+ _emit 0x0f
+ _emit 0x31
+ shr eax,8
+ shl edx,24
+ or eax, edx
+ mov dword ptr [ret_val], eax
+ }
+ return ret_val;
}
- return ret_val;
-}
-// return full timer value, *not* shifted by 8 bits
-inline U64 LLFastTimer::getCPUClockCount64()
-{
- U64 ret_val;
- __asm
+ // return full timer value, *not* shifted by 8 bits
+ static U64 getCPUClockCount64()
{
- _emit 0x0f
- _emit 0x31
- mov eax,eax
- mov edx,edx
- mov dword ptr [ret_val+4], edx
- mov dword ptr [ret_val], eax
+ U64 ret_val;
+ __asm
+ {
+ _emit 0x0f
+ _emit 0x31
+ mov eax,eax
+ mov edx,edx
+ mov dword ptr [ret_val+4], edx
+ mov dword ptr [ret_val], eax
+ }
+ return ret_val;
}
- return ret_val;
-}
+
+#else
+ //U64 get_clock_count(); // in lltimer.cpp
+ // These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures.
+ static U32 getCPUClockCount32()
+ {
+ return (U32)(get_clock_count()>>8);
+ }
+
+ static U64 getCPUClockCount64()
+ {
+ return get_clock_count();
+ }
+
#endif
+#endif
+
+
+#if (LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
+ //
+ // Linux and Solaris implementation of CPU clock - non-x86.
+ // This is accurate but SLOW! Only use out of desperation.
+ //
+ // Try to use the MONOTONIC clock if available, this is a constant time counter
+ // with nanosecond resolution (but not necessarily accuracy) and attempts are
+ // made to synchronize this value between cores at kernel start. It should not
+ // be affected by CPU frequency. If not available use the REALTIME clock, but
+ // this may be affected by NTP adjustments or other user activity affecting
+ // the system time.
+ static U64 getCPUClockCount64()
+ {
+ struct timespec tp;
-#if LL_LINUX || LL_SOLARIS
-//
-// Linux and Solaris implementation of CPU clock - all architectures.
-//
-// Try to use the MONOTONIC clock if available, this is a constant time counter
-// with nanosecond resolution (but not necessarily accuracy) and attempts are made
-// to synchronize this value between cores at kernel start. It should not be affected
-// by CPU frequency. If not available use the REALTIME clock, but this may be affected by
-// NTP adjustments or other user activity affecting the system time.
-inline U64 LLFastTimer::getCPUClockCount64()
-{
- struct timespec tp;
-
#ifdef CLOCK_MONOTONIC // MONOTONIC supported at build-time?
- if (-1 == clock_gettime(CLOCK_MONOTONIC,&tp)) // if MONOTONIC isn't supported at runtime then ouch, try REALTIME
+ if (-1 == clock_gettime(CLOCK_MONOTONIC,&tp)) // if MONOTONIC isn't supported at runtime then ouch, try REALTIME
#endif
- clock_gettime(CLOCK_REALTIME,&tp);
+ clock_gettime(CLOCK_REALTIME,&tp);
- return (tp.tv_sec*LLFastTimer::sClockResolution)+tp.tv_nsec;
-}
+ return (tp.tv_sec*sClockResolution)+tp.tv_nsec;
+ }
+
+ static U32 getCPUClockCount32()
+ {
+ return (U32)(getCPUClockCount64() >> 8);
+ }
+
+#endif // (LL_LINUX || LL_SOLARIS) && !(defined(__i386__) || defined(__amd64__))
-inline U32 LLFastTimer::getCPUClockCount32()
-{
- return (U32)(LLFastTimer::getCPUClockCount64() >> 8);
-}
-#endif // (LL_LINUX || LL_SOLARIS))
+#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
+ //
+ // Mac+Linux+Solaris FAST x86 implementation of CPU clock
+ static U32 getCPUClockCount32()
+ {
+ U64 x;
+ __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+ return (U32)(x >> 8);
+ }
+
+ static U64 getCPUClockCount64()
+ {
+ U64 x;
+ __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+ return x;
+ }
+
+#endif
+
+ static BlockTimerStatHandle& getRootTimeBlock();
+ static void pushLog(LLSD sd);
+ static void setLogLock(class LLMutex* mutex);
+ static void writeLog(std::ostream& os);
+ static void updateTimes();
+
+ static U64 countsPerSecond();
+
+ // updates cumulative times and hierarchy,
+ // can be called multiple times in a frame, at any point
+ static void processTimes();
+
+ static void bootstrapTimerTree();
+ static void incrementalUpdateTimerTree();
-#if (LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
-//
-// Mac x86 implementation of CPU clock
-inline U32 LLFastTimer::getCPUClockCount32()
+ // call this once a frame to periodically log timers
+ static void logStats();
+
+ // dumps current cumulative frame stats to log
+ // call nextFrame() to reset timers
+ static void dumpCurTimes();
+
+private:
+ friend class BlockTimerStatHandle;
+ // FIXME: this friendship exists so that each thread can instantiate a root timer,
+ // which could be a derived class with a public constructor instead, possibly
+ friend class ThreadRecorder;
+ friend BlockTimer timeThisBlock(BlockTimerStatHandle&);
+
+ BlockTimer(BlockTimerStatHandle& timer);
+#if !defined(MSC_VER) || MSC_VER < 1700
+ // Visual Studio 2010 has a bug where capturing an object returned by value
+ // into a local reference requires access to the copy constructor at the call site.
+ // This appears to be fixed in 2012.
+public:
+#endif
+ // no-copy
+ BlockTimer(const BlockTimer& other) {};
+
+private:
+ U64 mStartTime;
+ BlockTimerStackRecord mParentTimerData;
+
+public:
+ // statics
+ static std::string sLogName;
+ static bool sMetricLog,
+ sLog;
+ static U64 sClockResolution;
+
+};
+
+// this dummy function assists in allocating a block timer with stack-based lifetime.
+// this is done by capturing the return value in a stack-allocated const reference variable.
+// (This is most easily done using the macro LL_RECORD_BLOCK_TIME)
+// Otherwise, it would be possible to store a BlockTimer on the heap, resulting in non-nested lifetimes,
+// which would break the invariants of the timing hierarchy logic
+LL_FORCE_INLINE class BlockTimer timeThisBlock(class BlockTimerStatHandle& timer)
{
- U64 x;
- __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
- return (U32)(x >> 8);
+ return BlockTimer(timer);
}
-inline U64 LLFastTimer::getCPUClockCount64()
+// stores a "named" timer instance to be reused via multiple BlockTimer stack instances
+class BlockTimerStatHandle
+: public StatType<TimeBlockAccumulator>
{
- U64 x;
- __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
- return x;
-}
-#endif
+public:
+ BlockTimerStatHandle(const char* name, const char* description = "");
+ TimeBlockTreeNode& getTreeNode() const;
+ BlockTimerStatHandle* getParent() const { return getTreeNode().getParent(); }
+ void setParent(BlockTimerStatHandle* parent) { getTreeNode().setParent(parent); }
-#if ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__)))
-//
-// Mac PPC (deprecated) implementation of CPU clock
-//
-// Just use gettimeofday implementation for now
+ typedef std::vector<BlockTimerStatHandle*>::iterator child_iter;
+ typedef std::vector<BlockTimerStatHandle*>::const_iterator child_const_iter;
+ child_iter beginChildren();
+ child_iter endChildren();
+ bool hasChildren();
+ std::vector<BlockTimerStatHandle*>& getChildren();
-inline U32 LLFastTimer::getCPUClockCount32()
+ StatType<TimeBlockAccumulator::CallCountFacet>& callCount()
+ {
+ return static_cast<StatType<TimeBlockAccumulator::CallCountFacet>&>(*(StatType<TimeBlockAccumulator>*)this);
+ }
+
+ StatType<TimeBlockAccumulator::SelfTimeFacet>& selfTime()
+ {
+ return static_cast<StatType<TimeBlockAccumulator::SelfTimeFacet>&>(*(StatType<TimeBlockAccumulator>*)this);
+ }
+
+ bool mCollapsed; // don't show children
+};
+
+// iterators and helper functions for walking the call hierarchy of block timers in different ways
+typedef LLTreeDFSIter<BlockTimerStatHandle, BlockTimerStatHandle::child_const_iter> block_timer_tree_df_iterator_t;
+typedef LLTreeDFSPostIter<BlockTimerStatHandle, BlockTimerStatHandle::child_const_iter> block_timer_tree_df_post_iterator_t;
+typedef LLTreeBFSIter<BlockTimerStatHandle, BlockTimerStatHandle::child_const_iter> block_timer_tree_bf_iterator_t;
+
+block_timer_tree_df_iterator_t begin_block_timer_tree_df(BlockTimerStatHandle& id);
+block_timer_tree_df_iterator_t end_block_timer_tree_df();
+block_timer_tree_df_post_iterator_t begin_block_timer_tree_df_post(BlockTimerStatHandle& id);
+block_timer_tree_df_post_iterator_t end_block_timer_tree_df_post();
+block_timer_tree_bf_iterator_t begin_block_timer_tree_bf(BlockTimerStatHandle& id);
+block_timer_tree_bf_iterator_t end_block_timer_tree_bf();
+
+LL_FORCE_INLINE BlockTimer::BlockTimer(BlockTimerStatHandle& timer)
{
- return (U32)(get_clock_count()>>8);
+#if LL_FAST_TIMER_ON
+ BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
+ if (!cur_timer_data) return;
+ TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator();
+ accumulator.mActiveCount++;
+ // keep current parent as long as it is active when we are
+ accumulator.mMoveUpTree |= (accumulator.mParent->getCurrentAccumulator().mActiveCount == 0);
+
+ // store top of stack
+ mParentTimerData = *cur_timer_data;
+ // push new information
+ cur_timer_data->mActiveTimer = this;
+ cur_timer_data->mTimeBlock = &timer;
+ cur_timer_data->mChildTime = 0;
+
+ mStartTime = getCPUClockCount64();
+#endif
}
-inline U64 LLFastTimer::getCPUClockCount64()
+LL_FORCE_INLINE BlockTimer::~BlockTimer()
{
- return get_clock_count();
-}
+#if LL_FAST_TIMER_ON
+ U64 total_time = getCPUClockCount64() - mStartTime;
+ BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
+ if (!cur_timer_data) return;
+
+ TimeBlockAccumulator& accumulator = cur_timer_data->mTimeBlock->getCurrentAccumulator();
+
+ accumulator.mCalls++;
+ accumulator.mTotalTimeCounter += total_time;
+ accumulator.mSelfTimeCounter += total_time - cur_timer_data->mChildTime;
+ accumulator.mActiveCount--;
+
+ // store last caller to bootstrap tree creation
+ // do this in the destructor in case of recursion to get topmost caller
+ accumulator.mLastCaller = mParentTimerData.mTimeBlock;
+
+ // we are only tracking self time, so subtract our total time delta from parents
+ mParentTimerData.mChildTime += total_time;
+
+ //pop stack
+ *cur_timer_data = mParentTimerData;
#endif
+}
+
+}
+
+typedef LLTrace::BlockTimer LLFastTimer;
#endif // LL_LLFASTTIMER_H
diff --git a/indra/llcommon/llfasttimer_class.cpp b/indra/llcommon/llfasttimer_class.cpp
deleted file mode 100644
index 2e5edb1f3b..0000000000
--- a/indra/llcommon/llfasttimer_class.cpp
+++ /dev/null
@@ -1,768 +0,0 @@
-/**
- * @file llfasttimer_class.cpp
- * @brief Implementation of the fast timer.
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2007, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS."LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#include "linden_common.h"
-
-#include "llfasttimer.h"
-
-#include "llmemory.h"
-#include "llprocessor.h"
-#include "llsingleton.h"
-#include "lltreeiterators.h"
-#include "llsdserialize.h"
-
-#include <boost/bind.hpp>
-
-#if LL_WINDOWS
-#elif LL_LINUX || LL_SOLARIS
-#include <sys/time.h>
-#include <sched.h>
-#elif LL_DARWIN
-#include <sys/time.h>
-#include "lltimer.h" // get_clock_count()
-#else
-#error "architecture not supported"
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-// statics
-
-S32 LLFastTimer::sCurFrameIndex = -1;
-S32 LLFastTimer::sLastFrameIndex = -1;
-U64 LLFastTimer::sLastFrameTime = LLFastTimer::getCPUClockCount64();
-bool LLFastTimer::sPauseHistory = 0;
-bool LLFastTimer::sResetHistory = 0;
-LLFastTimer::CurTimerData LLFastTimer::sCurTimerData;
-BOOL LLFastTimer::sLog = FALSE;
-BOOL LLFastTimer::sMetricLog = FALSE;
-LLMutex* LLFastTimer::sLogLock = NULL;
-std::queue<LLSD> LLFastTimer::sLogQueue;
-
-#if LL_LINUX || LL_SOLARIS
-U64 LLFastTimer::sClockResolution = 1000000000; // Nanosecond resolution
-#else
-U64 LLFastTimer::sClockResolution = 1000000; // Microsecond resolution
-#endif
-
-std::vector<LLFastTimer::FrameState>* LLFastTimer::sTimerInfos = NULL;
-U64 LLFastTimer::sTimerCycles = 0;
-U32 LLFastTimer::sTimerCalls = 0;
-
-
-// FIXME: move these declarations to the relevant modules
-
-// helper functions
-typedef LLTreeDFSPostIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_bottom_up_iterator_t;
-
-static timer_tree_bottom_up_iterator_t begin_timer_tree_bottom_up(LLFastTimer::NamedTimer& id)
-{
- return timer_tree_bottom_up_iterator_t(&id,
- boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
- boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
-}
-
-static timer_tree_bottom_up_iterator_t end_timer_tree_bottom_up()
-{
- return timer_tree_bottom_up_iterator_t();
-}
-
-typedef LLTreeDFSIter<LLFastTimer::NamedTimer, LLFastTimer::NamedTimer::child_const_iter> timer_tree_dfs_iterator_t;
-
-
-static timer_tree_dfs_iterator_t begin_timer_tree(LLFastTimer::NamedTimer& id)
-{
- return timer_tree_dfs_iterator_t(&id,
- boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::beginChildren), _1),
- boost::bind(boost::mem_fn(&LLFastTimer::NamedTimer::endChildren), _1));
-}
-
-static timer_tree_dfs_iterator_t end_timer_tree()
-{
- return timer_tree_dfs_iterator_t();
-}
-
-
-
-// factory class that creates NamedTimers via static DeclareTimer objects
-class NamedTimerFactory : public LLSingleton<NamedTimerFactory>
-{
-public:
- NamedTimerFactory()
- : mActiveTimerRoot(NULL),
- mTimerRoot(NULL),
- mAppTimer(NULL),
- mRootFrameState(NULL)
- {}
-
- /*virtual */ void initSingleton()
- {
- mTimerRoot = new LLFastTimer::NamedTimer("root");
-
- mActiveTimerRoot = new LLFastTimer::NamedTimer("Frame");
- mActiveTimerRoot->setCollapsed(false);
-
- mRootFrameState = new LLFastTimer::FrameState(mActiveTimerRoot);
- mRootFrameState->mParent = &mTimerRoot->getFrameState();
- mActiveTimerRoot->setParent(mTimerRoot);
-
- mAppTimer = new LLFastTimer(mRootFrameState);
- }
-
- ~NamedTimerFactory()
- {
- std::for_each(mTimers.begin(), mTimers.end(), DeletePairedPointer());
-
- delete mAppTimer;
- delete mActiveTimerRoot;
- delete mTimerRoot;
- delete mRootFrameState;
- }
-
- LLFastTimer::NamedTimer& createNamedTimer(const std::string& name)
- {
- timer_map_t::iterator found_it = mTimers.find(name);
- if (found_it != mTimers.end())
- {
- return *found_it->second;
- }
-
- LLFastTimer::NamedTimer* timer = new LLFastTimer::NamedTimer(name);
- timer->setParent(mTimerRoot);
- mTimers.insert(std::make_pair(name, timer));
-
- return *timer;
- }
-
- LLFastTimer::NamedTimer* getTimerByName(const std::string& name)
- {
- timer_map_t::iterator found_it = mTimers.find(name);
- if (found_it != mTimers.end())
- {
- return found_it->second;
- }
- return NULL;
- }
-
- LLFastTimer::NamedTimer* getActiveRootTimer() { return mActiveTimerRoot; }
- LLFastTimer::NamedTimer* getRootTimer() { return mTimerRoot; }
- const LLFastTimer* getAppTimer() { return mAppTimer; }
- LLFastTimer::FrameState& getRootFrameState() { return *mRootFrameState; }
-
- typedef std::map<std::string, LLFastTimer::NamedTimer*> timer_map_t;
- timer_map_t::iterator beginTimers() { return mTimers.begin(); }
- timer_map_t::iterator endTimers() { return mTimers.end(); }
- S32 timerCount() { return mTimers.size(); }
-
-private:
- timer_map_t mTimers;
-
- LLFastTimer::NamedTimer* mActiveTimerRoot;
- LLFastTimer::NamedTimer* mTimerRoot;
- LLFastTimer* mAppTimer;
- LLFastTimer::FrameState* mRootFrameState;
-};
-
-void update_cached_pointers_if_changed()
-{
- // detect when elements have moved and update cached pointers
- static LLFastTimer::FrameState* sFirstTimerAddress = NULL;
- if (&*(LLFastTimer::getFrameStateList().begin()) != sFirstTimerAddress)
- {
- LLFastTimer::DeclareTimer::updateCachedPointers();
- }
- sFirstTimerAddress = &*(LLFastTimer::getFrameStateList().begin());
-}
-
-LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name, bool open )
-: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
-{
- mTimer.setCollapsed(!open);
- mFrameState = &mTimer.getFrameState();
- update_cached_pointers_if_changed();
-}
-
-LLFastTimer::DeclareTimer::DeclareTimer(const std::string& name)
-: mTimer(NamedTimerFactory::instance().createNamedTimer(name))
-{
- mFrameState = &mTimer.getFrameState();
- update_cached_pointers_if_changed();
-}
-
-// static
-void LLFastTimer::DeclareTimer::updateCachedPointers()
-{
- DeclareTimer::LLInstanceTrackerScopedGuard guard;
- // propagate frame state pointers to timer declarations
- for (DeclareTimer::instance_iter it = guard.beginInstances();
- it != guard.endInstances();
- ++it)
- {
- // update cached pointer
- it->mFrameState = &it->mTimer.getFrameState();
- }
-}
-
-//static
-#if LL_LINUX || LL_SOLARIS || ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__)) )
-U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
-{
- return sClockResolution >> 8;
-}
-#else // windows or x86-mac
-U64 LLFastTimer::countsPerSecond() // counts per second for the *32-bit* timer
-{
- static U64 sCPUClockFrequency = U64(CProcessor().GetCPUFrequency(50));
-
- // we drop the low-order byte in out timers, so report a lower frequency
- return sCPUClockFrequency >> 8;
-}
-#endif
-
-LLFastTimer::FrameState::FrameState(LLFastTimer::NamedTimer* timerp)
-: mActiveCount(0),
- mCalls(0),
- mSelfTimeCounter(0),
- mParent(NULL),
- mLastCaller(NULL),
- mMoveUpTree(false),
- mTimer(timerp)
-{}
-
-
-LLFastTimer::NamedTimer::NamedTimer(const std::string& name)
-: mName(name),
- mCollapsed(true),
- mParent(NULL),
- mTotalTimeCounter(0),
- mCountAverage(0),
- mCallAverage(0),
- mNeedsSorting(false)
-{
- info_list_t& frame_state_list = getFrameStateList();
- mFrameStateIndex = frame_state_list.size();
- getFrameStateList().push_back(FrameState(this));
-
- mCountHistory = new U32[HISTORY_NUM];
- memset(mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
- mCallHistory = new U32[HISTORY_NUM];
- memset(mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
-}
-
-LLFastTimer::NamedTimer::~NamedTimer()
-{
- delete[] mCountHistory;
- delete[] mCallHistory;
-}
-
-std::string LLFastTimer::NamedTimer::getToolTip(S32 history_idx)
-{
- if (history_idx < 0)
- {
- // by default, show average number of calls
- return llformat("%s (%d calls)", getName().c_str(), (S32)getCallAverage());
- }
- else
- {
- return llformat("%s (%d calls)", getName().c_str(), (S32)getHistoricalCalls(history_idx));
- }
-}
-
-void LLFastTimer::NamedTimer::setParent(NamedTimer* parent)
-{
- llassert_always(parent != this);
- llassert_always(parent != NULL);
-
- if (mParent)
- {
- // subtract our accumulated from previous parent
- for (S32 i = 0; i < HISTORY_NUM; i++)
- {
- mParent->mCountHistory[i] -= mCountHistory[i];
- }
-
- // subtract average timing from previous parent
- mParent->mCountAverage -= mCountAverage;
-
- std::vector<NamedTimer*>& children = mParent->getChildren();
- std::vector<NamedTimer*>::iterator found_it = std::find(children.begin(), children.end(), this);
- if (found_it != children.end())
- {
- children.erase(found_it);
- }
- }
-
- mParent = parent;
- if (parent)
- {
- getFrameState().mParent = &parent->getFrameState();
- parent->getChildren().push_back(this);
- parent->mNeedsSorting = true;
- }
-}
-
-S32 LLFastTimer::NamedTimer::getDepth()
-{
- S32 depth = 0;
- NamedTimer* timerp = mParent;
- while(timerp)
- {
- depth++;
- timerp = timerp->mParent;
- }
- return depth;
-}
-
-// static
-void LLFastTimer::NamedTimer::processTimes()
-{
- if (sCurFrameIndex < 0) return;
-
- buildHierarchy();
- accumulateTimings();
-}
-
-// sort timer info structs by depth first traversal order
-struct SortTimersDFS
-{
- bool operator()(const LLFastTimer::FrameState& i1, const LLFastTimer::FrameState& i2)
- {
- return i1.mTimer->getFrameStateIndex() < i2.mTimer->getFrameStateIndex();
- }
-};
-
-// sort child timers by name
-struct SortTimerByName
-{
- bool operator()(const LLFastTimer::NamedTimer* i1, const LLFastTimer::NamedTimer* i2)
- {
- return i1->getName() < i2->getName();
- }
-};
-
-//static
-void LLFastTimer::NamedTimer::buildHierarchy()
-{
- if (sCurFrameIndex < 0 ) return;
-
- // set up initial tree
- {
- NamedTimer::LLInstanceTrackerScopedGuard guard;
- for (instance_iter it = guard.beginInstances();
- it != guard.endInstances();
- ++it)
- {
- NamedTimer& timer = *it;
- if (&timer == NamedTimerFactory::instance().getRootTimer()) continue;
-
- // bootstrap tree construction by attaching to last timer to be on stack
- // when this timer was called
- if (timer.getFrameState().mLastCaller && timer.mParent == NamedTimerFactory::instance().getRootTimer())
- {
- timer.setParent(timer.getFrameState().mLastCaller->mTimer);
- // no need to push up tree on first use, flag can be set spuriously
- timer.getFrameState().mMoveUpTree = false;
- }
- }
- }
-
- // bump timers up tree if they've been flagged as being in the wrong place
- // do this in a bottom up order to promote descendants first before promoting ancestors
- // this preserves partial order derived from current frame's observations
- for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getRootTimer());
- it != end_timer_tree_bottom_up();
- ++it)
- {
- NamedTimer* timerp = *it;
- // skip root timer
- if (timerp == NamedTimerFactory::instance().getRootTimer()) continue;
-
- if (timerp->getFrameState().mMoveUpTree)
- {
- // since ancestors have already been visited, reparenting won't affect tree traversal
- //step up tree, bringing our descendants with us
- //llinfos << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() <<
- // " to child of " << timerp->getParent()->getParent()->getName() << llendl;
- timerp->setParent(timerp->getParent()->getParent());
- timerp->getFrameState().mMoveUpTree = false;
-
- // don't bubble up any ancestors until descendants are done bubbling up
- it.skipAncestors();
- }
- }
-
- // sort timers by time last called, so call graph makes sense
- for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
- it != end_timer_tree();
- ++it)
- {
- NamedTimer* timerp = (*it);
- if (timerp->mNeedsSorting)
- {
- std::sort(timerp->getChildren().begin(), timerp->getChildren().end(), SortTimerByName());
- }
- timerp->mNeedsSorting = false;
- }
-}
-
-//static
-void LLFastTimer::NamedTimer::accumulateTimings()
-{
- U32 cur_time = getCPUClockCount32();
-
- // walk up stack of active timers and accumulate current time while leaving timing structures active
- LLFastTimer* cur_timer = sCurTimerData.mCurTimer;
- // root defined by parent pointing to self
- CurTimerData* cur_data = &sCurTimerData;
- while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
- {
- U32 cumulative_time_delta = cur_time - cur_timer->mStartTime;
- U32 self_time_delta = cumulative_time_delta - cur_data->mChildTime;
- cur_data->mChildTime = 0;
- cur_timer->mFrameState->mSelfTimeCounter += self_time_delta;
- cur_timer->mStartTime = cur_time;
-
- cur_data = &cur_timer->mLastTimerData;
- cur_data->mChildTime += cumulative_time_delta;
-
- cur_timer = cur_timer->mLastTimerData.mCurTimer;
- }
-
- // traverse tree in DFS post order, or bottom up
- for(timer_tree_bottom_up_iterator_t it = begin_timer_tree_bottom_up(*NamedTimerFactory::instance().getActiveRootTimer());
- it != end_timer_tree_bottom_up();
- ++it)
- {
- NamedTimer* timerp = (*it);
- timerp->mTotalTimeCounter = timerp->getFrameState().mSelfTimeCounter;
- for (child_const_iter child_it = timerp->beginChildren(); child_it != timerp->endChildren(); ++child_it)
- {
- timerp->mTotalTimeCounter += (*child_it)->mTotalTimeCounter;
- }
-
- S32 cur_frame = sCurFrameIndex;
- if (cur_frame >= 0)
- {
- // update timer history
- int hidx = cur_frame % HISTORY_NUM;
-
- timerp->mCountHistory[hidx] = timerp->mTotalTimeCounter;
- timerp->mCountAverage = (timerp->mCountAverage * cur_frame + timerp->mTotalTimeCounter) / (cur_frame+1);
- timerp->mCallHistory[hidx] = timerp->getFrameState().mCalls;
- timerp->mCallAverage = (timerp->mCallAverage * cur_frame + timerp->getFrameState().mCalls) / (cur_frame+1);
- }
- }
-}
-
-// static
-void LLFastTimer::NamedTimer::resetFrame()
-{
- if (sLog)
- { //output current frame counts to performance log
- F64 iclock_freq = 1000.0 / countsPerSecond(); // good place to calculate clock frequency
-
- F64 total_time = 0;
- LLSD sd;
-
- {
- NamedTimer::LLInstanceTrackerScopedGuard guard;
- for (NamedTimer::instance_iter it = guard.beginInstances();
- it != guard.endInstances();
- ++it)
- {
- NamedTimer& timer = *it;
- FrameState& info = timer.getFrameState();
- sd[timer.getName()]["Time"] = (LLSD::Real) (info.mSelfTimeCounter*iclock_freq);
- sd[timer.getName()]["Calls"] = (LLSD::Integer) info.mCalls;
-
- // computing total time here because getting the root timer's getCountHistory
- // doesn't work correctly on the first frame
- total_time = total_time + info.mSelfTimeCounter * iclock_freq;
- }
- }
-
- sd["Total"]["Time"] = (LLSD::Real) total_time;
- sd["Total"]["Calls"] = (LLSD::Integer) 1;
-
- {
- LLMutexLock lock(sLogLock);
- sLogQueue.push(sd);
- }
- }
-
-
- // tag timers by position in depth first traversal of tree
- S32 index = 0;
- for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
- it != end_timer_tree();
- ++it)
- {
- NamedTimer* timerp = (*it);
-
- timerp->mFrameStateIndex = index;
- index++;
-
- llassert_always(timerp->mFrameStateIndex < (S32)getFrameStateList().size());
- }
-
- // sort timers by dfs traversal order to improve cache coherency
- std::sort(getFrameStateList().begin(), getFrameStateList().end(), SortTimersDFS());
-
- // update pointers into framestatelist now that we've sorted it
- DeclareTimer::updateCachedPointers();
-
- // reset for next frame
- {
- NamedTimer::LLInstanceTrackerScopedGuard guard;
- for (NamedTimer::instance_iter it = guard.beginInstances();
- it != guard.endInstances();
- ++it)
- {
- NamedTimer& timer = *it;
-
- FrameState& info = timer.getFrameState();
- info.mSelfTimeCounter = 0;
- info.mCalls = 0;
- info.mLastCaller = NULL;
- info.mMoveUpTree = false;
- // update parent pointer in timer state struct
- if (timer.mParent)
- {
- info.mParent = &timer.mParent->getFrameState();
- }
- }
- }
-
- //sTimerCycles = 0;
- //sTimerCalls = 0;
-}
-
-//static
-void LLFastTimer::NamedTimer::reset()
-{
- resetFrame(); // reset frame data
-
- // walk up stack of active timers and reset start times to current time
- // effectively zeroing out any accumulated time
- U32 cur_time = getCPUClockCount32();
-
- // root defined by parent pointing to self
- CurTimerData* cur_data = &sCurTimerData;
- LLFastTimer* cur_timer = cur_data->mCurTimer;
- while(cur_timer->mLastTimerData.mCurTimer != cur_timer)
- {
- cur_timer->mStartTime = cur_time;
- cur_data->mChildTime = 0;
-
- cur_data = &cur_timer->mLastTimerData;
- cur_timer = cur_data->mCurTimer;
- }
-
- // reset all history
- {
- NamedTimer::LLInstanceTrackerScopedGuard guard;
- for (NamedTimer::instance_iter it = guard.beginInstances();
- it != guard.endInstances();
- ++it)
- {
- NamedTimer& timer = *it;
- if (&timer != NamedTimerFactory::instance().getRootTimer())
- {
- timer.setParent(NamedTimerFactory::instance().getRootTimer());
- }
-
- timer.mCountAverage = 0;
- timer.mCallAverage = 0;
- memset(timer.mCountHistory, 0, sizeof(U32) * HISTORY_NUM);
- memset(timer.mCallHistory, 0, sizeof(U32) * HISTORY_NUM);
- }
- }
-
- sLastFrameIndex = 0;
- sCurFrameIndex = 0;
-}
-
-//static
-LLFastTimer::info_list_t& LLFastTimer::getFrameStateList()
-{
- if (!sTimerInfos)
- {
- sTimerInfos = new info_list_t();
- }
- return *sTimerInfos;
-}
-
-
-U32 LLFastTimer::NamedTimer::getHistoricalCount(S32 history_index) const
-{
- S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
- return mCountHistory[history_idx];
-}
-
-U32 LLFastTimer::NamedTimer::getHistoricalCalls(S32 history_index ) const
-{
- S32 history_idx = (getLastFrameIndex() + history_index) % LLFastTimer::NamedTimer::HISTORY_NUM;
- return mCallHistory[history_idx];
-}
-
-LLFastTimer::FrameState& LLFastTimer::NamedTimer::getFrameState() const
-{
- llassert_always(mFrameStateIndex >= 0);
- if (this == NamedTimerFactory::instance().getActiveRootTimer())
- {
- return NamedTimerFactory::instance().getRootFrameState();
- }
- return getFrameStateList()[mFrameStateIndex];
-}
-
-// static
-LLFastTimer::NamedTimer& LLFastTimer::NamedTimer::getRootNamedTimer()
-{
- return *NamedTimerFactory::instance().getActiveRootTimer();
-}
-
-std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::beginChildren()
-{
- return mChildren.begin();
-}
-
-std::vector<LLFastTimer::NamedTimer*>::const_iterator LLFastTimer::NamedTimer::endChildren()
-{
- return mChildren.end();
-}
-
-std::vector<LLFastTimer::NamedTimer*>& LLFastTimer::NamedTimer::getChildren()
-{
- return mChildren;
-}
-
-//static
-void LLFastTimer::nextFrame()
-{
- countsPerSecond(); // good place to calculate clock frequency
- U64 frame_time = getCPUClockCount64();
- if ((frame_time - sLastFrameTime) >> 8 > 0xffffffff)
- {
- llinfos << "Slow frame, fast timers inaccurate" << llendl;
- }
-
- if (sPauseHistory)
- {
- sResetHistory = true;
- }
- else if (sResetHistory)
- {
- sLastFrameIndex = 0;
- sCurFrameIndex = 0;
- sResetHistory = false;
- }
- else // not paused
- {
- NamedTimer::processTimes();
- sLastFrameIndex = sCurFrameIndex++;
- }
-
- // get ready for next frame
- NamedTimer::resetFrame();
- sLastFrameTime = frame_time;
-}
-
-//static
-void LLFastTimer::dumpCurTimes()
-{
- // accumulate timings, etc.
- NamedTimer::processTimes();
-
- F64 clock_freq = (F64)countsPerSecond();
- F64 iclock_freq = 1000.0 / clock_freq; // clock_ticks -> milliseconds
-
- // walk over timers in depth order and output timings
- for(timer_tree_dfs_iterator_t it = begin_timer_tree(*NamedTimerFactory::instance().getRootTimer());
- it != end_timer_tree();
- ++it)
- {
- NamedTimer* timerp = (*it);
- F64 total_time_ms = ((F64)timerp->getHistoricalCount(0) * iclock_freq);
- // Don't bother with really brief times, keep output concise
- if (total_time_ms < 0.1) continue;
-
- std::ostringstream out_str;
- for (S32 i = 0; i < timerp->getDepth(); i++)
- {
- out_str << "\t";
- }
-
-
- out_str << timerp->getName() << " "
- << std::setprecision(3) << total_time_ms << " ms, "
- << timerp->getHistoricalCalls(0) << " calls";
-
- llinfos << out_str.str() << llendl;
- }
-}
-
-//static
-void LLFastTimer::reset()
-{
- NamedTimer::reset();
-}
-
-
-//static
-void LLFastTimer::writeLog(std::ostream& os)
-{
- while (!sLogQueue.empty())
- {
- LLSD& sd = sLogQueue.front();
- LLSDSerialize::toXML(sd, os);
- LLMutexLock lock(sLogLock);
- sLogQueue.pop();
- }
-}
-
-//static
-const LLFastTimer::NamedTimer* LLFastTimer::getTimerByName(const std::string& name)
-{
- return NamedTimerFactory::instance().getTimerByName(name);
-}
-
-LLFastTimer::LLFastTimer(LLFastTimer::FrameState* state)
-: mFrameState(state)
-{
- U32 start_time = getCPUClockCount32();
- mStartTime = start_time;
- mFrameState->mActiveCount++;
- LLFastTimer::sCurTimerData.mCurTimer = this;
- LLFastTimer::sCurTimerData.mFrameState = mFrameState;
- LLFastTimer::sCurTimerData.mChildTime = 0;
- mLastTimerData = LLFastTimer::sCurTimerData;
-}
-
-
-//////////////////////////////////////////////////////////////////////////////
diff --git a/indra/llcommon/llfasttimer_class.h b/indra/llcommon/llfasttimer_class.h
deleted file mode 100644
index ddb1a74793..0000000000
--- a/indra/llcommon/llfasttimer_class.h
+++ /dev/null
@@ -1,272 +0,0 @@
-/**
- * @file llfasttimer_class.h
- * @brief Declaration of a fast timer.
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_FASTTIMER_CLASS_H
-#define LL_FASTTIMER_CLASS_H
-
-#include "llinstancetracker.h"
-
-#define FAST_TIMER_ON 1
-#define TIME_FAST_TIMERS 0
-
-class LLMutex;
-
-#include <queue>
-#include "llsd.h"
-
-class LL_COMMON_API LLFastTimer
-{
-public:
- class NamedTimer;
-
- struct LL_COMMON_API FrameState
- {
- FrameState(NamedTimer* timerp);
-
- U32 mSelfTimeCounter;
- U32 mCalls;
- FrameState* mParent; // info for caller timer
- FrameState* mLastCaller; // used to bootstrap tree construction
- NamedTimer* mTimer;
- U16 mActiveCount; // number of timers with this ID active on stack
- bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
- };
-
- // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
- class LL_COMMON_API NamedTimer
- : public LLInstanceTracker<NamedTimer>
- {
- friend class DeclareTimer;
- public:
- ~NamedTimer();
-
- enum { HISTORY_NUM = 60 };
-
- const std::string& getName() const { return mName; }
- NamedTimer* getParent() const { return mParent; }
- void setParent(NamedTimer* parent);
- S32 getDepth();
- std::string getToolTip(S32 history_index = -1);
-
- typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
- child_const_iter beginChildren();
- child_const_iter endChildren();
- std::vector<NamedTimer*>& getChildren();
-
- void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
- bool getCollapsed() const { return mCollapsed; }
-
- U32 getCountAverage() const { return mCountAverage; }
- U32 getCallAverage() const { return mCallAverage; }
-
- U32 getHistoricalCount(S32 history_index = 0) const;
- U32 getHistoricalCalls(S32 history_index = 0) const;
-
- static NamedTimer& getRootNamedTimer();
-
- S32 getFrameStateIndex() const { return mFrameStateIndex; }
-
- FrameState& getFrameState() const;
-
- private:
- friend class LLFastTimer;
- friend class NamedTimerFactory;
-
- //
- // methods
- //
- NamedTimer(const std::string& name);
- // recursive call to gather total time from children
- static void accumulateTimings();
-
- // updates cumulative times and hierarchy,
- // can be called multiple times in a frame, at any point
- static void processTimes();
-
- static void buildHierarchy();
- static void resetFrame();
- static void reset();
-
- //
- // members
- //
- S32 mFrameStateIndex;
-
- std::string mName;
-
- U32 mTotalTimeCounter;
-
- U32 mCountAverage;
- U32 mCallAverage;
-
- U32* mCountHistory;
- U32* mCallHistory;
-
- // tree structure
- NamedTimer* mParent; // NamedTimer of caller(parent)
- std::vector<NamedTimer*> mChildren;
- bool mCollapsed; // don't show children
- bool mNeedsSorting; // sort children whenever child added
- };
-
- // used to statically declare a new named timer
- class LL_COMMON_API DeclareTimer
- : public LLInstanceTracker<DeclareTimer>
- {
- friend class LLFastTimer;
- public:
- DeclareTimer(const std::string& name, bool open);
- DeclareTimer(const std::string& name);
-
- static void updateCachedPointers();
-
- private:
- NamedTimer& mTimer;
- FrameState* mFrameState;
- };
-
-public:
- LLFastTimer(LLFastTimer::FrameState* state);
-
- LL_FORCE_INLINE LLFastTimer(LLFastTimer::DeclareTimer& timer)
- : mFrameState(timer.mFrameState)
- {
-#if TIME_FAST_TIMERS
- U64 timer_start = getCPUClockCount64();
-#endif
-#if FAST_TIMER_ON
- LLFastTimer::FrameState* frame_state = mFrameState;
- mStartTime = getCPUClockCount32();
-
- frame_state->mActiveCount++;
- frame_state->mCalls++;
- // keep current parent as long as it is active when we are
- frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
-
- LLFastTimer::CurTimerData* cur_timer_data = &LLFastTimer::sCurTimerData;
- mLastTimerData = *cur_timer_data;
- cur_timer_data->mCurTimer = this;
- cur_timer_data->mFrameState = frame_state;
- cur_timer_data->mChildTime = 0;
-#endif
-#if TIME_FAST_TIMERS
- U64 timer_end = getCPUClockCount64();
- sTimerCycles += timer_end - timer_start;
-#endif
- }
-
- LL_FORCE_INLINE ~LLFastTimer()
- {
-#if TIME_FAST_TIMERS
- U64 timer_start = getCPUClockCount64();
-#endif
-#if FAST_TIMER_ON
- LLFastTimer::FrameState* frame_state = mFrameState;
- U32 total_time = getCPUClockCount32() - mStartTime;
-
- frame_state->mSelfTimeCounter += total_time - LLFastTimer::sCurTimerData.mChildTime;
- frame_state->mActiveCount--;
-
- // store last caller to bootstrap tree creation
- // do this in the destructor in case of recursion to get topmost caller
- frame_state->mLastCaller = mLastTimerData.mFrameState;
-
- // we are only tracking self time, so subtract our total time delta from parents
- mLastTimerData.mChildTime += total_time;
-
- LLFastTimer::sCurTimerData = mLastTimerData;
-#endif
-#if TIME_FAST_TIMERS
- U64 timer_end = getCPUClockCount64();
- sTimerCycles += timer_end - timer_start;
- sTimerCalls++;
-#endif
- }
-
-public:
- static LLMutex* sLogLock;
- static std::queue<LLSD> sLogQueue;
- static BOOL sLog;
- static BOOL sMetricLog;
- static bool sPauseHistory;
- static bool sResetHistory;
- static U64 sTimerCycles;
- static U32 sTimerCalls;
-
- typedef std::vector<FrameState> info_list_t;
- static info_list_t& getFrameStateList();
-
-
- // call this once a frame to reset timers
- static void nextFrame();
-
- // dumps current cumulative frame stats to log
- // call nextFrame() to reset timers
- static void dumpCurTimes();
-
- // call this to reset timer hierarchy, averages, etc.
- static void reset();
-
- static U64 countsPerSecond();
- static S32 getLastFrameIndex() { return sLastFrameIndex; }
- static S32 getCurFrameIndex() { return sCurFrameIndex; }
-
- static void writeLog(std::ostream& os);
- static const NamedTimer* getTimerByName(const std::string& name);
-
- struct CurTimerData
- {
- LLFastTimer* mCurTimer;
- FrameState* mFrameState;
- U32 mChildTime;
- };
- static CurTimerData sCurTimerData;
-
-private:
- static U32 getCPUClockCount32();
- static U64 getCPUClockCount64();
- static U64 sClockResolution;
-
- static S32 sCurFrameIndex;
- static S32 sLastFrameIndex;
- static U64 sLastFrameTime;
- static info_list_t* sTimerInfos;
-
- U32 mStartTime;
- LLFastTimer::FrameState* mFrameState;
- LLFastTimer::CurTimerData mLastTimerData;
-
-};
-
-typedef class LLFastTimer LLFastTimer;
-
-#endif // LL_LLFASTTIMER_CLASS_H
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index 2a76f7fb83..295c97eac8 100644..100755
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -1,73 +1,203 @@
-/**
+/**
* @file llfile.cpp
* @author Michael Schlachter
* @date 2006-03-23
* @brief Implementation of cross-platform POSIX file buffer and c++
* stream classes.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if LL_WINDOWS
-#include <windows.h>
+#include "llwin32headerslean.h"
+#include <stdlib.h> // Windows errno
+#else
+#include <errno.h>
#endif
#include "linden_common.h"
#include "llfile.h"
#include "llstring.h"
#include "llerror.h"
+#include "stringize.h"
using namespace std;
+static std::string empty;
+
+// Many of the methods below use OS-level functions that mess with errno. Wrap
+// variants of strerror() to report errors.
+
+#if LL_WINDOWS
+// On Windows, use strerror_s().
+std::string strerr(int errn)
+{
+ char buffer[256];
+ strerror_s(buffer, errn); // infers sizeof(buffer) -- love it!
+ return buffer;
+}
+
+typedef std::basic_ios<char,std::char_traits < char > > _Myios;
+
+#else
+// On Posix we want to call strerror_r(), but alarmingly, there are two
+// different variants. The one that returns int always populates the passed
+// buffer (except in case of error), whereas the other one always returns a
+// valid char* but might or might not populate the passed buffer. How do we
+// know which one we're getting? Define adapters for each and let the compiler
+// select the applicable adapter.
+
+// strerror_r() returns char*
+std::string message_from(int /*orig_errno*/, const char* /*buffer*/, size_t /*bufflen*/,
+ const char* strerror_ret)
+{
+ return strerror_ret;
+}
+
+// strerror_r() returns int
+std::string message_from(int orig_errno, const char* buffer, size_t bufflen,
+ int strerror_ret)
+{
+ if (strerror_ret == 0)
+ {
+ return buffer;
+ }
+ // Here strerror_r() has set errno. Since strerror_r() has already failed,
+ // seems like a poor bet to call it again to diagnose its own error...
+ int stre_errno = errno;
+ if (stre_errno == ERANGE)
+ {
+ return STRINGIZE("strerror_r() can't explain errno " << orig_errno
+ << " (" << bufflen << "-byte buffer too small)");
+ }
+ if (stre_errno == EINVAL)
+ {
+ return STRINGIZE("unknown errno " << orig_errno);
+ }
+ // Here we don't even understand the errno from strerror_r()!
+ return STRINGIZE("strerror_r() can't explain errno " << orig_errno
+ << " (error " << stre_errno << ')');
+}
+
+std::string strerr(int errn)
+{
+ char buffer[256];
+ // Select message_from() function matching the strerror_r() we have on hand.
+ return message_from(errn, buffer, sizeof(buffer),
+ strerror_r(errn, buffer, sizeof(buffer)));
+}
+#endif // ! LL_WINDOWS
+
+// On either system, shorthand call just infers global 'errno'.
+std::string strerr()
+{
+ return strerr(errno);
+}
+
+int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0)
+{
+ if (rc < 0)
+ {
+ // Capture errno before we start emitting output
+ int errn = errno;
+ // For certain operations, a particular errno value might be
+ // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit
+ // EEXIST. Don't warn if caller explicitly says this errno is okay.
+ if (errn != accept)
+ {
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
+ << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
+ }
+#if 0 && LL_WINDOWS // turn on to debug file-locking problems
+ // If the problem is "Permission denied," maybe it's because another
+ // process has the file open. Try to find out.
+ if (errn == EACCES) // *not* EPERM
+ {
+ // Only do any of this stuff (before LL_ENDL) if it will be logged.
+ LL_DEBUGS("LLFile") << empty;
+ const char* TEMP = getenv("TEMP");
+ if (! TEMP)
+ {
+ LL_CONT << "No $TEMP, not running 'handle'";
+ }
+ else
+ {
+ std::string tf(TEMP);
+ tf += "\\handle.tmp";
+ // http://technet.microsoft.com/en-us/sysinternals/bb896655
+ std::string cmd(STRINGIZE("handle \"" << filename
+ // "openfiles /query /v | fgrep -i \"" << filename
+ << "\" > \"" << tf << '"'));
+ LL_CONT << cmd;
+ if (system(cmd.c_str()) != 0)
+ {
+ LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655";
+ }
+ else
+ {
+ std::ifstream inf(tf);
+ std::string line;
+ while (std::getline(inf, line))
+ {
+ LL_CONT << '\n' << line;
+ }
+ }
+ LLFile::remove(tf);
+ }
+ LL_CONT << LL_ENDL;
+ }
+#endif // LL_WINDOWS hack to identify processes holding file open
+ }
+ return rc;
+}
+
// static
int LLFile::mkdir(const std::string& dirname, int perms)
{
-#if LL_WINDOWS
+#if LL_WINDOWS
// permissions are ignored on Windows
std::string utf8dirname = dirname;
llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
- return _wmkdir(utf16dirname.c_str());
+ int rc = _wmkdir(utf16dirname.c_str());
#else
- return ::mkdir(dirname.c_str(), (mode_t)perms);
+ int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
#endif
+ // We often use mkdir() to ensure the existence of a directory that might
+ // already exist. Don't spam the log if it does.
+ return warnif("mkdir", dirname, rc, EEXIST);
}
// static
int LLFile::rmdir(const std::string& dirname)
{
-#if LL_WINDOWS
+#if LL_WINDOWS
// permissions are ignored on Windows
std::string utf8dirname = dirname;
llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname);
- return _wrmdir(utf16dirname.c_str());
+ int rc = _wrmdir(utf16dirname.c_str());
#else
- return ::rmdir(dirname.c_str());
+ int rc = ::rmdir(dirname.c_str());
#endif
+ return warnif("rmdir", dirname, rc);
}
// static
@@ -98,15 +228,27 @@ LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int shari
#endif
}
+int LLFile::close(LLFILE * file)
+{
+ int ret_value = 0;
+ if (file)
+ {
+ ret_value = fclose(file);
+ }
+ return ret_value;
+}
+
+
int LLFile::remove(const std::string& filename)
{
#if LL_WINDOWS
std::string utf8filename = filename;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- return _wremove(utf16filename.c_str());
+ int rc = _wremove(utf16filename.c_str());
#else
- return ::remove(filename.c_str());
+ int rc = ::remove(filename.c_str());
#endif
+ return warnif("remove", filename, rc);
}
int LLFile::rename(const std::string& filename, const std::string& newname)
@@ -116,10 +258,42 @@ int LLFile::rename(const std::string& filename, const std::string& newname)
std::string utf8newname = newname;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
llutf16string utf16newname = utf8str_to_utf16str(utf8newname);
- return _wrename(utf16filename.c_str(),utf16newname.c_str());
+ int rc = _wrename(utf16filename.c_str(),utf16newname.c_str());
#else
- return ::rename(filename.c_str(),newname.c_str());
+ int rc = ::rename(filename.c_str(),newname.c_str());
#endif
+ return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc);
+}
+
+bool LLFile::copy(const std::string from, const std::string to)
+{
+ bool copied = false;
+ LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */
+ if (in)
+ {
+ LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */
+ if (out)
+ {
+ char buf[16384]; /* Flawfinder: ignore */
+ size_t readbytes;
+ bool write_ok = true;
+ while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */
+ {
+ if (fwrite(buf, 1, readbytes, out) != readbytes)
+ {
+ LL_WARNS("LLFile") << "Short write" << LL_ENDL;
+ write_ok = false;
+ }
+ }
+ if ( write_ok )
+ {
+ copied = true;
+ }
+ fclose(out);
+ }
+ fclose(in);
+ }
+ return copied;
}
int LLFile::stat(const std::string& filename, llstat* filestatus)
@@ -127,23 +301,26 @@ int LLFile::stat(const std::string& filename, llstat* filestatus)
#if LL_WINDOWS
std::string utf8filename = filename;
llutf16string utf16filename = utf8str_to_utf16str(utf8filename);
- return _wstat(utf16filename.c_str(),filestatus);
+ int rc = _wstat(utf16filename.c_str(),filestatus);
#else
- return ::stat(filename.c_str(),filestatus);
+ int rc = ::stat(filename.c_str(),filestatus);
#endif
+ // We use stat() to determine existence (see isfile(), isdir()).
+ // Don't spam the log if the subject pathname doesn't exist.
+ return warnif("stat", filename, rc, ENOENT);
}
bool LLFile::isdir(const std::string& filename)
{
llstat st;
-
+
return stat(filename, &st) == 0 && S_ISDIR(st.st_mode);
}
bool LLFile::isfile(const std::string& filename)
{
llstat st;
-
+
return stat(filename, &st) == 0 && S_ISREG(st.st_mode);
}
@@ -180,9 +357,10 @@ const char *LLFile::tmpdir()
/***************** Modified file stream created to overcome the incorrect behaviour of posix fopen in windows *******************/
-#if USE_LLFILESTREAMS
+#if LL_WINDOWS
-LLFILE * LLFile::_Fiopen(const std::string& filename, std::ios::openmode mode,int) // protection currently unused
+LLFILE * LLFile::_Fiopen(const std::string& filename,
+ std::ios::openmode mode)
{ // open a file
static const char *mods[] =
{ // fopen mode strings corresponding to valid[i]
@@ -241,112 +419,554 @@ LLFILE * LLFile::_Fiopen(const std::string& filename, std::ios::openmode mode,in
return (0);
}
-/************** input file stream ********************************/
+#endif /* LL_WINDOWS */
-void llifstream::close()
-{ // close the C stream
- if (_Filebuffer && _Filebuffer->close() == 0)
+/************** llstdio file buffer ********************************/
+
+
+#if !LL_WINDOWS
+llstdio_filebuf::int_type llstdio_filebuf::overflow(llstdio_filebuf::int_type __c)
+{
+ int_type __ret = traits_type::eof();
+ const bool __testeof = traits_type::eq_int_type(__c, __ret);
+ const bool __testout = _M_mode & ios_base::out;
+ if (__testout && !_M_reading)
{
- _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ if (this->pbase() < this->pptr())
+ {
+ // If appropriate, append the overflow char.
+ if (!__testeof)
+ {
+ *this->pptr() = traits_type::to_char_type(__c);
+ this->pbump(1);
+ }
+
+ // Convert pending sequence to external representation,
+ // and output.
+ if (_convert_to_external(this->pbase(),
+ this->pptr() - this->pbase()))
+ {
+ _M_set_buffer(0);
+ __ret = traits_type::not_eof(__c);
+ }
+ }
+ else if (_M_buf_size > 1)
+ {
+ // Overflow in 'uncommitted' mode: set _M_writing, set
+ // the buffer to the initial 'write' mode, and put __c
+ // into the buffer.
+ _M_set_buffer(0);
+ _M_writing = true;
+ if (!__testeof)
+ {
+ *this->pptr() = traits_type::to_char_type(__c);
+ this->pbump(1);
+ }
+ __ret = traits_type::not_eof(__c);
+ }
+ else
+ {
+ // Unbuffered.
+ char_type __conv = traits_type::to_char_type(__c);
+ if (__testeof || _convert_to_external(&__conv, 1))
+ {
+ _M_writing = true;
+ __ret = traits_type::not_eof(__c);
+ }
+ }
}
+ return __ret;
}
-void llifstream::open(const std::string& _Filename, /* Flawfinder: ignore */
- ios_base::openmode _Mode,
- int _Prot)
-{ // open a C stream with specified mode
-
- LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::in, _Prot);
- if(filep == NULL)
+bool llstdio_filebuf::_convert_to_external(char_type* __ibuf,
+ std::streamsize __ilen)
+{
+ // Sizes of external and pending output.
+ streamsize __elen;
+ streamsize __plen;
+ if (__check_facet(_M_codecvt).always_noconv())
{
- _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
- return;
+ //__elen = _M_file.xsputn(reinterpret_cast<char*>(__ibuf), __ilen);
+ __elen = fwrite(reinterpret_cast<void*>(__ibuf), 1,
+ __ilen, _M_file.file());
+ __plen = __ilen;
}
- llassert(_Filebuffer == NULL);
- _Filebuffer = new _Myfb(filep);
- _ShouldClose = true;
- _Myios::init(_Filebuffer);
+ else
+ {
+ // Worst-case number of external bytes needed.
+ // XXX Not done encoding() == -1.
+ streamsize __blen = __ilen * _M_codecvt->max_length();
+ char* __buf = static_cast<char*>(__builtin_alloca(__blen));
+
+ char* __bend;
+ const char_type* __iend;
+ codecvt_base::result __r;
+ __r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen,
+ __iend, __buf, __buf + __blen, __bend);
+
+ if (__r == codecvt_base::ok || __r == codecvt_base::partial)
+ __blen = __bend - __buf;
+ else if (__r == codecvt_base::noconv)
+ {
+ // Same as the always_noconv case above.
+ __buf = reinterpret_cast<char*>(__ibuf);
+ __blen = __ilen;
+ }
+ else
+ __throw_ios_failure(__N("llstdio_filebuf::_convert_to_external "
+ "conversion error"));
+
+ //__elen = _M_file.xsputn(__buf, __blen);
+ __elen = fwrite(__buf, 1, __blen, _M_file.file());
+ __plen = __blen;
+
+ // Try once more for partial conversions.
+ if (__r == codecvt_base::partial && __elen == __plen)
+ {
+ const char_type* __iresume = __iend;
+ streamsize __rlen = this->pptr() - __iend;
+ __r = _M_codecvt->out(_M_state_cur, __iresume,
+ __iresume + __rlen, __iend, __buf,
+ __buf + __blen, __bend);
+ if (__r != codecvt_base::error)
+ {
+ __rlen = __bend - __buf;
+ //__elen = _M_file.xsputn(__buf, __rlen);
+ __elen = fwrite(__buf, 1, __rlen, _M_file.file());
+ __plen = __rlen;
+ }
+ else
+ {
+ __throw_ios_failure(__N("llstdio_filebuf::_convert_to_external "
+ "conversion error"));
+ }
+ }
+ }
+ return __elen == __plen;
}
-bool llifstream::is_open() const
-{ // test if C stream has been opened
- if(_Filebuffer)
- return (_Filebuffer->is_open());
- return false;
+llstdio_filebuf::int_type llstdio_filebuf::underflow()
+{
+ int_type __ret = traits_type::eof();
+ const bool __testin = _M_mode & ios_base::in;
+ if (__testin)
+ {
+ if (_M_writing)
+ {
+ if (overflow() == traits_type::eof())
+ return __ret;
+ //_M_set_buffer(-1);
+ //_M_writing = false;
+ }
+ // Check for pback madness, and if so switch back to the
+ // normal buffers and jet outta here before expensive
+ // fileops happen...
+ _M_destroy_pback();
+
+ if (this->gptr() < this->egptr())
+ return traits_type::to_int_type(*this->gptr());
+
+ // Get and convert input sequence.
+ const size_t __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1;
+
+ // Will be set to true if ::fread() returns 0 indicating EOF.
+ bool __got_eof = false;
+ // Number of internal characters produced.
+ streamsize __ilen = 0;
+ codecvt_base::result __r = codecvt_base::ok;
+ if (__check_facet(_M_codecvt).always_noconv())
+ {
+ //__ilen = _M_file.xsgetn(reinterpret_cast<char*>(this->eback()),
+ // __buflen);
+ __ilen = fread(reinterpret_cast<void*>(this->eback()), 1,
+ __buflen, _M_file.file());
+ if (__ilen == 0)
+ __got_eof = true;
+ }
+ else
+ {
+ // Worst-case number of external bytes.
+ // XXX Not done encoding() == -1.
+ const int __enc = _M_codecvt->encoding();
+ streamsize __blen; // Minimum buffer size.
+ streamsize __rlen; // Number of chars to read.
+ if (__enc > 0)
+ __blen = __rlen = __buflen * __enc;
+ else
+ {
+ __blen = __buflen + _M_codecvt->max_length() - 1;
+ __rlen = __buflen;
+ }
+ const streamsize __remainder = _M_ext_end - _M_ext_next;
+ __rlen = __rlen > __remainder ? __rlen - __remainder : 0;
+
+ // An imbue in 'read' mode implies first converting the external
+ // chars already present.
+ if (_M_reading && this->egptr() == this->eback() && __remainder)
+ __rlen = 0;
+
+ // Allocate buffer if necessary and move unconverted
+ // bytes to front.
+ if (_M_ext_buf_size < __blen)
+ {
+ char* __buf = new char[__blen];
+ if (__remainder)
+ __builtin_memcpy(__buf, _M_ext_next, __remainder);
+
+ delete [] _M_ext_buf;
+ _M_ext_buf = __buf;
+ _M_ext_buf_size = __blen;
+ }
+ else if (__remainder)
+ __builtin_memmove(_M_ext_buf, _M_ext_next, __remainder);
+
+ _M_ext_next = _M_ext_buf;
+ _M_ext_end = _M_ext_buf + __remainder;
+ _M_state_last = _M_state_cur;
+
+ do
+ {
+ if (__rlen > 0)
+ {
+ // Sanity check!
+ // This may fail if the return value of
+ // codecvt::max_length() is bogus.
+ if (_M_ext_end - _M_ext_buf + __rlen > _M_ext_buf_size)
+ {
+ __throw_ios_failure(__N("llstdio_filebuf::underflow "
+ "codecvt::max_length() "
+ "is not valid"));
+ }
+ //streamsize __elen = _M_file.xsgetn(_M_ext_end, __rlen);
+ streamsize __elen = fread(_M_ext_end, 1,
+ __rlen, _M_file.file());
+ if (__elen == 0)
+ __got_eof = true;
+ else if (__elen == -1)
+ break;
+ //_M_ext_end += __elen;
+ }
+
+ char_type* __iend = this->eback();
+ if (_M_ext_next < _M_ext_end)
+ {
+ __r = _M_codecvt->in(_M_state_cur, _M_ext_next,
+ _M_ext_end, _M_ext_next,
+ this->eback(),
+ this->eback() + __buflen, __iend);
+ }
+ if (__r == codecvt_base::noconv)
+ {
+ size_t __avail = _M_ext_end - _M_ext_buf;
+ __ilen = std::min(__avail, __buflen);
+ traits_type::copy(this->eback(),
+ reinterpret_cast<char_type*>
+ (_M_ext_buf), __ilen);
+ _M_ext_next = _M_ext_buf + __ilen;
+ }
+ else
+ __ilen = __iend - this->eback();
+
+ // _M_codecvt->in may return error while __ilen > 0: this is
+ // ok, and actually occurs in case of mixed encodings (e.g.,
+ // XML files).
+ if (__r == codecvt_base::error)
+ break;
+
+ __rlen = 1;
+ } while (__ilen == 0 && !__got_eof);
+ }
+
+ if (__ilen > 0)
+ {
+ _M_set_buffer(__ilen);
+ _M_reading = true;
+ __ret = traits_type::to_int_type(*this->gptr());
+ }
+ else if (__got_eof)
+ {
+ // If the actual end of file is reached, set 'uncommitted'
+ // mode, thus allowing an immediate write without an
+ // intervening seek.
+ _M_set_buffer(-1);
+ _M_reading = false;
+ // However, reaching it while looping on partial means that
+ // the file has got an incomplete character.
+ if (__r == codecvt_base::partial)
+ __throw_ios_failure(__N("llstdio_filebuf::underflow "
+ "incomplete character in file"));
+ }
+ else if (__r == codecvt_base::error)
+ __throw_ios_failure(__N("llstdio_filebuf::underflow "
+ "invalid byte sequence in file"));
+ else
+ __throw_ios_failure(__N("llstdio_filebuf::underflow "
+ "error reading the file"));
+ }
+ return __ret;
}
-llifstream::~llifstream()
-{
- if (_ShouldClose)
+
+std::streamsize llstdio_filebuf::xsgetn(char_type* __s, std::streamsize __n)
+{
+ // Clear out pback buffer before going on to the real deal...
+ streamsize __ret = 0;
+ if (_M_pback_init)
{
- close();
+ if (__n > 0 && this->gptr() == this->eback())
+ {
+ *__s++ = *this->gptr();
+ this->gbump(1);
+ __ret = 1;
+ --__n;
+ }
+ _M_destroy_pback();
+ }
+
+ // Optimization in the always_noconv() case, to be generalized in the
+ // future: when __n > __buflen we read directly instead of using the
+ // buffer repeatedly.
+ const bool __testin = _M_mode & ios_base::in;
+ const streamsize __buflen = _M_buf_size > 1 ? _M_buf_size - 1 : 1;
+
+ if (__n > __buflen && __check_facet(_M_codecvt).always_noconv()
+ && __testin && !_M_writing)
+ {
+ // First, copy the chars already present in the buffer.
+ const streamsize __avail = this->egptr() - this->gptr();
+ if (__avail != 0)
+ {
+ if (__avail == 1)
+ *__s = *this->gptr();
+ else
+ traits_type::copy(__s, this->gptr(), __avail);
+ __s += __avail;
+ this->gbump(__avail);
+ __ret += __avail;
+ __n -= __avail;
+ }
+
+ // Need to loop in case of short reads (relatively common
+ // with pipes).
+ streamsize __len;
+ for (;;)
+ {
+ //__len = _M_file.xsgetn(reinterpret_cast<char*>(__s), __n);
+ __len = fread(reinterpret_cast<void*>(__s), 1,
+ __n, _M_file.file());
+ if (__len == -1)
+ __throw_ios_failure(__N("llstdio_filebuf::xsgetn "
+ "error reading the file"));
+ if (__len == 0)
+ break;
+
+ __n -= __len;
+ __ret += __len;
+ if (__n == 0)
+ break;
+
+ __s += __len;
+ }
+
+ if (__n == 0)
+ {
+ _M_set_buffer(0);
+ _M_reading = true;
+ }
+ else if (__len == 0)
+ {
+ // If end of file is reached, set 'uncommitted'
+ // mode, thus allowing an immediate write without
+ // an intervening seek.
+ _M_set_buffer(-1);
+ _M_reading = false;
+ }
}
- delete _Filebuffer;
+ else
+ __ret += __streambuf_type::xsgetn(__s, __n);
+
+ return __ret;
}
-llifstream::llifstream(const std::string& _Filename,
- ios_base::openmode _Mode,
- int _Prot)
- : std::basic_istream< char , std::char_traits< char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)
+std::streamsize llstdio_filebuf::xsputn(const char_type* __s, std::streamsize __n)
+{
+ // Optimization in the always_noconv() case, to be generalized in the
+ // future: when __n is sufficiently large we write directly instead of
+ // using the buffer.
+ streamsize __ret = 0;
+ const bool __testout = _M_mode & ios_base::out;
+ if (__check_facet(_M_codecvt).always_noconv()
+ && __testout && !_M_reading)
+ {
+ // Measurement would reveal the best choice.
+ const streamsize __chunk = 1ul << 10;
+ streamsize __bufavail = this->epptr() - this->pptr();
+
+ // Don't mistake 'uncommitted' mode buffered with unbuffered.
+ if (!_M_writing && _M_buf_size > 1)
+ __bufavail = _M_buf_size - 1;
-{ // construct with named file and specified mode
- open(_Filename, _Mode | ios_base::in, _Prot); /* Flawfinder: ignore */
+ const streamsize __limit = std::min(__chunk, __bufavail);
+ if (__n >= __limit)
+ {
+ const streamsize __buffill = this->pptr() - this->pbase();
+ const char* __buf = reinterpret_cast<const char*>(this->pbase());
+ //__ret = _M_file.xsputn_2(__buf, __buffill,
+ // reinterpret_cast<const char*>(__s), __n);
+ if (__buffill)
+ {
+ __ret = fwrite(__buf, 1, __buffill, _M_file.file());
+ }
+ if (__ret == __buffill)
+ {
+ __ret += fwrite(reinterpret_cast<const char*>(__s), 1,
+ __n, _M_file.file());
+ }
+ if (__ret == __buffill + __n)
+ {
+ _M_set_buffer(0);
+ _M_writing = true;
+ }
+ if (__ret > __buffill)
+ __ret -= __buffill;
+ else
+ __ret = 0;
+ }
+ else
+ __ret = __streambuf_type::xsputn(__s, __n);
+ }
+ else
+ __ret = __streambuf_type::xsputn(__s, __n);
+ return __ret;
}
+int llstdio_filebuf::sync()
+{
+ return (_M_file.sync() == 0 ? 0 : -1);
+}
+#endif
-/************** output file stream ********************************/
+#if LL_WINDOWS
+/************** input file stream ********************************/
-bool llofstream::is_open() const
+llifstream::llifstream() :
+ _M_filebuf(),
+ std::istream(&_M_filebuf)
+{
+}
+
+// explicit
+llifstream::llifstream(const std::string& _Filename,
+ ios_base::openmode _Mode) :
+ _M_filebuf(),
+ std::istream(&_M_filebuf)
+{
+ llutf16string wideName = utf8str_to_utf16str( _Filename );
+ if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0)
+ {
+ _Myios::setstate(ios_base::failbit);
+ }
+}
+
+// explicit
+llifstream::llifstream(const char* _Filename,
+ ios_base::openmode _Mode) :
+ _M_filebuf(),
+ std::istream(&_M_filebuf)
+{
+ llutf16string wideName = utf8str_to_utf16str( _Filename );
+ if (_M_filebuf.open(wideName.c_str(), _Mode | ios_base::in) == 0)
+ {
+ _Myios::setstate(ios_base::failbit);
+ }
+}
+
+bool llifstream::is_open() const
{ // test if C stream has been opened
- if(_Filebuffer)
- return (_Filebuffer->is_open());
- return false;
+ return _M_filebuf.is_open();
}
-void llofstream::open(const std::string& _Filename, /* Flawfinder: ignore */
- ios_base::openmode _Mode,
- int _Prot)
+void llifstream::open(const char* _Filename, ios_base::openmode _Mode)
{ // open a C stream with specified mode
-
- LLFILE* filep = LLFile::_Fiopen(_Filename,_Mode | ios_base::out, _Prot);
- if(filep == NULL)
+ llutf16string wideName = utf8str_to_utf16str( _Filename );
+ if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::in) == 0)
{
- _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
- return;
+ _Myios::setstate(ios_base::failbit);
+ }
+ else
+ {
+ _Myios::clear();
}
- llassert(_Filebuffer==NULL);
- _Filebuffer = new _Myfb(filep);
- _ShouldClose = true;
- _Myios::init(_Filebuffer);
}
-void llofstream::close()
+void llifstream::close()
{ // close the C stream
- if(is_open())
+ if (_M_filebuf.close() == 0)
{
- if (_Filebuffer->close() == 0)
- _Myios::setstate(ios_base::failbit); /*Flawfinder: ignore*/
+ _Myios::setstate(ios_base::failbit);
}
}
+
+/************** output file stream ********************************/
+
+
+llofstream::llofstream() :
+ _M_filebuf(),
+ std::ostream(&_M_filebuf)
+{
+}
+
+// explicit
llofstream::llofstream(const std::string& _Filename,
- std::ios_base::openmode _Mode,
- int _Prot)
- : std::basic_ostream<char,std::char_traits < char > >(NULL,true),_Filebuffer(NULL),_ShouldClose(false)
-{ // construct with named file and specified mode
- open(_Filename, _Mode , _Prot); /* Flawfinder: ignore */
+ ios_base::openmode _Mode) :
+ _M_filebuf(),
+ std::ostream(&_M_filebuf)
+{
+ llutf16string wideName = utf8str_to_utf16str( _Filename );
+ if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
+ {
+ _Myios::setstate(ios_base::failbit);
+ }
}
-llofstream::~llofstream()
-{
- // destroy the object
- if (_ShouldClose)
+// explicit
+llofstream::llofstream(const char* _Filename,
+ ios_base::openmode _Mode) :
+ _M_filebuf(),
+ std::ostream(&_M_filebuf)
+{
+ llutf16string wideName = utf8str_to_utf16str( _Filename );
+ if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
{
- close();
+ _Myios::setstate(ios_base::failbit);
}
- delete _Filebuffer;
}
-#endif // #if USE_LLFILESTREAMS
+bool llofstream::is_open() const
+{ // test if C stream has been opened
+ return _M_filebuf.is_open();
+}
+
+void llofstream::open(const char* _Filename, ios_base::openmode _Mode)
+{ // open a C stream with specified mode
+ llutf16string wideName = utf8str_to_utf16str( _Filename );
+ if (_M_filebuf.open( wideName.c_str(), _Mode | ios_base::out) == 0)
+ {
+ _Myios::setstate(ios_base::failbit);
+ }
+ else
+ {
+ _Myios::clear();
+ }
+}
+
+void llofstream::close()
+{ // close the C stream
+ if (_M_filebuf.close() == 0)
+ {
+ _Myios::setstate(ios_base::failbit);
+ }
+}
/************** helper functions ********************************/
@@ -374,4 +994,4 @@ std::streamsize llofstream_size(llofstream& ofstr)
return pos_end - pos_beg;
}
-
+#endif // LL_WINDOWS
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index fea5d3ed2b..347c9867aa 100644..100755
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -5,31 +5,25 @@
* @brief Declaration of cross-platform POSIX file buffer and c++
* stream classes.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,13 +38,6 @@
typedef FILE LLFILE;
#include <fstream>
-
-#ifdef LL_WINDOWS
-#define USE_LLFILESTREAMS 1
-#else
-#define USE_LLFILESTREAMS 0
-#endif
-
#include <sys/stat.h>
#if LL_WINDOWS
@@ -58,6 +45,8 @@ typedef FILE LLFILE;
typedef struct _stat llstat;
#else
typedef struct stat llstat;
+#include <ext/stdio_filebuf.h>
+#include <bits/postypes.h>
#endif
#ifndef S_ISREG
@@ -77,6 +66,8 @@ public:
static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */
static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag);
+ static int close(LLFILE * file);
+
// 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 std::string& filename, int perms = 0700);
@@ -84,145 +75,295 @@ public:
static int rmdir(const std::string& filename);
static int remove(const std::string& filename);
static int rename(const std::string& filename,const std::string& newname);
+ static bool copy(const std::string from, const std::string to);
+
static int stat(const std::string& filename,llstat* file_status);
static bool isdir(const std::string& filename);
static bool isfile(const std::string& filename);
- static LLFILE * _Fiopen(const std::string& filename, std::ios::openmode mode,int); // protection currently unused
+ static LLFILE * _Fiopen(const std::string& filename,
+ std::ios::openmode mode);
static const char * tmpdir();
};
+/**
+ * @brief Provides a layer of compatibility for C/POSIX.
+ *
+ * This is taken from both the GNU __gnu_cxx::stdio_filebuf extension and
+ * VC's basic_filebuf implementation.
+ * This file buffer provides extensions for working with standard C FILE*'s
+ * and POSIX file descriptors for platforms that support this.
+*/
+namespace
+{
+#if LL_WINDOWS
+typedef std::filebuf _Myfb;
+#else
+typedef __gnu_cxx::stdio_filebuf< char > _Myfb;
+typedef std::__c_file _Filet;
+#endif /* LL_WINDOWS */
+}
-#if USE_LLFILESTREAMS
-
-class LL_COMMON_API llifstream : public std::basic_istream < char , std::char_traits < char > >
+class LL_COMMON_API llstdio_filebuf : public _Myfb
{
- // 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),_ShouldClose(false)
- { // construct unopened
- }
+ /**
+ * deferred initialization / destruction
+ */
+ llstdio_filebuf() : _Myfb() {}
+ virtual ~llstdio_filebuf() {}
+
+ /**
+ * @param f An open @c FILE*.
+ * @param mode Same meaning as in a standard filebuf.
+ * @param size Optimal or preferred size of internal buffer, in chars.
+ * Defaults to system's @c BUFSIZ.
+ *
+ * This constructor associates a file stream buffer with an open
+ * C @c FILE*. The @c FILE* will not be automatically closed when the
+ * stdio_filebuf is closed/destroyed.
+ */
+ llstdio_filebuf(_Filet* __f, std::ios_base::openmode __mode,
+ //size_t __size = static_cast<size_t>(BUFSIZ)) :
+ size_t __size = static_cast<size_t>(1)) :
+#if LL_WINDOWS
+ _Myfb(__f) {}
+#else
+ _Myfb(__f, __mode, __size) {}
+#endif
- explicit llifstream(const std::string& _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)),
- _ShouldClose(false)
- { // construct with specified C stream
- }
- virtual ~llifstream();
-
- _Myfb *rdbuf() const
- { // return pointer to file buffer
- return _Filebuffer;
- }
- bool is_open() const;
- void open(const std::string& _Filename, /* Flawfinder: ignore */
- ios_base::openmode _Mode = ios_base::in,
- int _Prot = (int)ios_base::_Openprot);
- void close();
+ /**
+ * @brief Opens an external file.
+ * @param s The name of the file.
+ * @param mode The open mode flags.
+ * @return @c this on success, NULL on failure
+ *
+ * If a file is already open, this function immediately fails.
+ * Otherwise it tries to open the file named @a s using the flags
+ * given in @a mode.
+ */
+ //llstdio_filebuf* open(const char *_Filename,
+ // std::ios_base::openmode _Mode);
+
+ /**
+ * @param fd An open file descriptor.
+ * @param mode Same meaning as in a standard filebuf.
+ * @param size Optimal or preferred size of internal buffer, in chars.
+ *
+ * This constructor associates a file stream buffer with an open
+ * POSIX file descriptor. The file descriptor will be automatically
+ * closed when the stdio_filebuf is closed/destroyed.
+ */
+#if !LL_WINDOWS
+ llstdio_filebuf(int __fd, std::ios_base::openmode __mode,
+ //size_t __size = static_cast<size_t>(BUFSIZ)) :
+ size_t __size = static_cast<size_t>(1)) :
+ _Myfb(__fd, __mode, __size) {}
+#endif
-private:
- _Myfb* _Filebuffer; // the file buffer
- bool _ShouldClose;
+// *TODO: Seek the underlying c stream for better cross-platform compatibility?
+#if !LL_WINDOWS
+protected:
+ /** underflow() and uflow() functions are called to get the next
+ * character from the real input source when the buffer is empty.
+ * Buffered input uses underflow()
+ */
+ /*virtual*/ int_type underflow();
+
+ /* Convert internal byte sequence to external, char-based
+ * sequence via codecvt.
+ */
+ bool _convert_to_external(char_type*, std::streamsize);
+
+ /** The overflow() function is called to transfer characters to the
+ * real output destination when the buffer is full. A call to
+ * overflow(c) outputs the contents of the buffer plus the
+ * character c.
+ * Consume some sequence of the characters in the pending sequence.
+ */
+ /*virtual*/ int_type overflow(int_type __c = traits_type::eof());
+
+ /** sync() flushes the underlying @c FILE* stream.
+ */
+ /*virtual*/ int sync();
+
+ std::streamsize xsgetn(char_type*, std::streamsize);
+ std::streamsize xsputn(const char_type*, std::streamsize);
+#endif
};
-
-class LL_COMMON_API llofstream : public std::basic_ostream< char , std::char_traits < char > >
+#if LL_WINDOWS
+/**
+ * @brief Controlling input for files.
+ *
+ * This class supports reading from named files, using the inherited
+ * functions from std::basic_istream. To control the associated
+ * sequence, an instance of std::basic_filebuf (or a platform-specific derivative)
+ * which allows construction using a pre-exisintg file stream buffer.
+ * We refer to this std::basic_filebuf (or derivative) as @c sb.
+ */
+class LL_COMMON_API llifstream : public std::istream
{
-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),_ShouldClose(false)
- { // construct unopened
- }
-
- explicit llofstream(const std::string& _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)
- _ShouldClose(false)
- { // construct with specified C stream
- }
-
- virtual ~llofstream();
-
- _Myfb *rdbuf() const
- { // return pointer to file buffer
- return _Filebuffer;
- }
-
+ // input stream associated with a C stream
+ public:
+ // Constructors:
+ /**
+ * @brief Default constructor.
+ *
+ * Initializes @c sb using its default constructor, and passes
+ * @c &sb to the base class initializer. Does not open any files
+ * (you haven't given it a filename to open).
+ */
+ llifstream();
+
+ /**
+ * @brief Create an input file stream.
+ * @param Filename String specifying the filename.
+ * @param Mode Open file in specified mode (see std::ios_base).
+ *
+ * @c ios_base::in is automatically included in @a mode.
+ */
+ explicit llifstream(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ explicit llifstream(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+
+ /**
+ * @brief The destructor does nothing.
+ *
+ * The file is closed by the filebuf object, not the formatting
+ * stream.
+ */
+ virtual ~llifstream() {}
+
+ // Members:
+ /**
+ * @brief Accessing the underlying buffer.
+ * @return The current basic_filebuf buffer.
+ *
+ * This hides both signatures of std::basic_ios::rdbuf().
+ */
+ llstdio_filebuf* rdbuf() const
+ { return const_cast<llstdio_filebuf*>(&_M_filebuf); }
+
+ /**
+ * @brief Wrapper to test for an open file.
+ * @return @c rdbuf()->is_open()
+ */
bool is_open() const;
- void open(const std::string& _Filename,ios_base::openmode _Mode = ios_base::out,int _Prot = (int)ios_base::_Openprot); /* Flawfinder: ignore */
-
+ /**
+ * @brief Opens an external file.
+ * @param Filename The name of the file.
+ * @param Node The open mode flags.
+ *
+ * Calls @c llstdio_filebuf::open(s,mode|in). If that function
+ * fails, @c failbit is set in the stream's error state.
+ */
+ void open(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::in)
+ { open(_Filename.c_str(), _Mode); }
+ void open(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+
+ /**
+ * @brief Close the file.
+ *
+ * Calls @c llstdio_filebuf::close(). If that function
+ * fails, @c failbit is set in the stream's error state.
+ */
void close();
-private:
- _Myfb *_Filebuffer; // the file buffer
- bool _ShouldClose;
-};
-
-
-
-#else
-//Use standard file streams on non windows platforms
-//#define llifstream std::ifstream
-//#define llofstream std::ofstream
-
-class LL_COMMON_API llifstream : public std::ifstream
-{
-public:
- llifstream() : std::ifstream()
- {
- }
-
- explicit llifstream(const std::string& _Filename, std::_Ios_Openmode _Mode = in)
- : std::ifstream(_Filename.c_str(), _Mode)
- {
- }
- void open(const std::string& _Filename, std::_Ios_Openmode _Mode = in) /* Flawfinder: ignore */
- {
- std::ifstream::open(_Filename.c_str(), _Mode);
- }
+ private:
+ llstdio_filebuf _M_filebuf;
};
-class LL_COMMON_API llofstream : public std::ofstream
+/**
+ * @brief Controlling output for files.
+ *
+ * This class supports writing to named files, using the inherited
+ * functions from std::basic_ostream. To control the associated
+ * sequence, an instance of std::basic_filebuf (or a platform-specific derivative)
+ * which allows construction using a pre-exisintg file stream buffer.
+ * We refer to this std::basic_filebuf (or derivative) as @c sb.
+*/
+class LL_COMMON_API llofstream : public std::ostream
{
-public:
- llofstream() : std::ofstream()
- {
- }
-
- explicit llofstream(const std::string& _Filename, std::_Ios_Openmode _Mode = out)
- : std::ofstream(_Filename.c_str(), _Mode)
- {
- }
+ public:
+ // Constructors:
+ /**
+ * @brief Default constructor.
+ *
+ * Initializes @c sb using its default constructor, and passes
+ * @c &sb to the base class initializer. Does not open any files
+ * (you haven't given it a filename to open).
+ */
+ llofstream();
+
+ /**
+ * @brief Create an output file stream.
+ * @param Filename String specifying the filename.
+ * @param Mode Open file in specified mode (see std::ios_base).
+ *
+ * @c ios_base::out|ios_base::trunc is automatically included in
+ * @a mode.
+ */
+ explicit llofstream(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+ explicit llofstream(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+
+ /**
+ * @brief The destructor does nothing.
+ *
+ * The file is closed by the filebuf object, not the formatting
+ * stream.
+ */
+ virtual ~llofstream() {}
+
+ // Members:
+ /**
+ * @brief Accessing the underlying buffer.
+ * @return The current basic_filebuf buffer.
+ *
+ * This hides both signatures of std::basic_ios::rdbuf().
+ */
+ llstdio_filebuf* rdbuf() const
+ { return const_cast<llstdio_filebuf*>(&_M_filebuf); }
+
+ /**
+ * @brief Wrapper to test for an open file.
+ * @return @c rdbuf()->is_open()
+ */
+ bool is_open() const;
- void open(const std::string& _Filename, std::_Ios_Openmode _Mode = out) /* Flawfinder: ignore */
- {
- std::ofstream::open(_Filename.c_str(), _Mode);
- }
+ /**
+ * @brief Opens an external file.
+ * @param Filename The name of the file.
+ * @param Node The open mode flags.
+ *
+ * Calls @c llstdio_filebuf::open(s,mode|out). If that function
+ * fails, @c failbit is set in the stream's error state.
+ */
+ void open(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc)
+ { open(_Filename.c_str(), _Mode); }
+ void open(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+
+ /**
+ * @brief Close the file.
+ *
+ * Calls @c llstdio_filebuf::close(). If that function
+ * fails, @c failbit is set in the stream's error state.
+ */
+ void close();
+ private:
+ llstdio_filebuf _M_filebuf;
};
-#endif
/**
* @breif filesize helpers.
@@ -234,4 +375,12 @@ public:
std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr);
std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr);
+#else // ! LL_WINDOWS
+
+// on non-windows, llifstream and llofstream are just mapped directly to the std:: equivalents
+typedef std::ifstream llifstream;
+typedef std::ofstream llofstream;
+
+#endif // LL_WINDOWS or ! LL_WINDOWS
+
#endif // not LL_LLFILE_H
diff --git a/indra/llcommon/llfindlocale.cpp b/indra/llcommon/llfindlocale.cpp
index 505f5c540b..f019bd0c64 100644..100755
--- a/indra/llcommon/llfindlocale.cpp
+++ b/indra/llcommon/llfindlocale.cpp
@@ -2,31 +2,25 @@
* @file llfindlocale.cpp
* @brief Detect system language setting
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,7 +33,7 @@
#include <ctype.h>
#ifdef WIN32
-#include <windows.h>
+#include "llwin32headers.h"
#include <winnt.h>
#endif
diff --git a/indra/llcommon/llfindlocale.h b/indra/llcommon/llfindlocale.h
index b812a065db..6770db5774 100644..100755
--- a/indra/llcommon/llfindlocale.h
+++ b/indra/llcommon/llfindlocale.h
@@ -2,31 +2,25 @@
* @file llfindlocale.h
* @brief Detect system language setting
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llfixedbuffer.cpp b/indra/llcommon/llfixedbuffer.cpp
index 1f6a06c247..d394f179fb 100644..100755
--- a/indra/llcommon/llfixedbuffer.cpp
+++ b/indra/llcommon/llfixedbuffer.cpp
@@ -1,31 +1,25 @@
/**
* @file llfixedbuffer.cpp
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
index 17fdef27d7..554cf48a4c 100644..100755
--- a/indra/llcommon/llfixedbuffer.h
+++ b/indra/llcommon/llfixedbuffer.h
@@ -2,31 +2,25 @@
* @file llfixedbuffer.h
* @brief A fixed size buffer of lines.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llfoldertype.cpp b/indra/llcommon/llfoldertype.cpp
deleted file mode 100644
index 2610fe9e6a..0000000000
--- a/indra/llcommon/llfoldertype.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/**
- * @file llfoldertype.cpp
- * @brief Implementatino of LLFolderType functionality.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llfoldertype.h"
-#include "lldictionary.h"
-#include "llmemory.h"
-#include "llsingleton.h"
-
-///----------------------------------------------------------------------------
-/// Class LLFolderType
-///----------------------------------------------------------------------------
-struct FolderEntry : public LLDictionaryEntry
-{
- FolderEntry(const std::string &type_name, // 8 character limit!
- bool is_protected) // can the viewer change categories of this type?
- :
- LLDictionaryEntry(type_name),
- mIsProtected(is_protected)
- {
- llassert(type_name.length() <= 8);
- }
-
- const bool mIsProtected;
-};
-
-class LLFolderDictionary : public LLSingleton<LLFolderDictionary>,
- public LLDictionary<LLFolderType::EType, FolderEntry>
-{
-public:
- LLFolderDictionary();
-protected:
- virtual LLFolderType::EType notFound() const
- {
- return LLFolderType::FT_NONE;
- }
-};
-
-LLFolderDictionary::LLFolderDictionary()
-{
- // TYPE NAME PROTECTED
- // |-----------|---------|
- addEntry(LLFolderType::FT_TEXTURE, new FolderEntry("texture", TRUE));
- addEntry(LLFolderType::FT_SOUND, new FolderEntry("sound", TRUE));
- addEntry(LLFolderType::FT_CALLINGCARD, new FolderEntry("callcard", TRUE));
- addEntry(LLFolderType::FT_LANDMARK, new FolderEntry("landmark", TRUE));
- addEntry(LLFolderType::FT_CLOTHING, new FolderEntry("clothing", TRUE));
- addEntry(LLFolderType::FT_OBJECT, new FolderEntry("object", TRUE));
- addEntry(LLFolderType::FT_NOTECARD, new FolderEntry("notecard", TRUE));
- addEntry(LLFolderType::FT_ROOT_INVENTORY, new FolderEntry("root_inv", TRUE));
- addEntry(LLFolderType::FT_LSL_TEXT, new FolderEntry("lsltext", TRUE));
- addEntry(LLFolderType::FT_BODYPART, new FolderEntry("bodypart", TRUE));
- addEntry(LLFolderType::FT_TRASH, new FolderEntry("trash", TRUE));
- addEntry(LLFolderType::FT_SNAPSHOT_CATEGORY, new FolderEntry("snapshot", TRUE));
- addEntry(LLFolderType::FT_LOST_AND_FOUND, new FolderEntry("lstndfnd", TRUE));
- addEntry(LLFolderType::FT_ANIMATION, new FolderEntry("animatn", TRUE));
- addEntry(LLFolderType::FT_GESTURE, new FolderEntry("gesture", TRUE));
- addEntry(LLFolderType::FT_FAVORITE, new FolderEntry("favorite", TRUE));
-
- for (S32 ensemble_num = S32(LLFolderType::FT_ENSEMBLE_START); ensemble_num <= S32(LLFolderType::FT_ENSEMBLE_END); ensemble_num++)
- {
- addEntry(LLFolderType::EType(ensemble_num), new FolderEntry("ensemble", FALSE));
- }
-
- addEntry(LLFolderType::FT_CURRENT_OUTFIT, new FolderEntry("current", TRUE));
- addEntry(LLFolderType::FT_OUTFIT, new FolderEntry("outfit", FALSE));
- addEntry(LLFolderType::FT_MY_OUTFITS, new FolderEntry("my_otfts", TRUE));
- addEntry(LLFolderType::FT_INBOX, new FolderEntry("inbox", TRUE));
-
- addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE));
-};
-
-// static
-LLFolderType::EType LLFolderType::lookup(const std::string& name)
-{
- return LLFolderDictionary::getInstance()->lookup(name);
-}
-
-// static
-const std::string &LLFolderType::lookup(LLFolderType::EType folder_type)
-{
- const FolderEntry *entry = LLFolderDictionary::getInstance()->lookup(folder_type);
- if (entry)
- {
- return entry->mName;
- }
- else
- {
- return badLookup();
- }
-}
-
-// static
-// Only ensembles and plain folders aren't protected. "Protected" means
-// you can't change certain properties such as their type.
-bool LLFolderType::lookupIsProtectedType(EType folder_type)
-{
- const LLFolderDictionary *dict = LLFolderDictionary::getInstance();
- const FolderEntry *entry = dict->lookup(folder_type);
- if (entry)
- {
- return entry->mIsProtected;
- }
- return true;
-}
-
-// static
-bool LLFolderType::lookupIsEnsembleType(EType folder_type)
-{
- return (folder_type >= FT_ENSEMBLE_START &&
- folder_type <= FT_ENSEMBLE_END);
-}
-
-// static
-LLAssetType::EType LLFolderType::folderTypeToAssetType(LLFolderType::EType folder_type)
-{
- if (LLAssetType::lookup(LLAssetType::EType(folder_type)) == LLAssetType::badLookup())
- {
- llwarns << "Converting to unknown asset type " << folder_type << llendl;
- }
- return (LLAssetType::EType)folder_type;
-}
-
-// static
-LLFolderType::EType LLFolderType::assetTypeToFolderType(LLAssetType::EType asset_type)
-{
- if (LLFolderType::lookup(LLFolderType::EType(asset_type)) == LLFolderType::badLookup())
- {
- llwarns << "Converting to unknown folder type " << asset_type << llendl;
- }
- return (LLFolderType::EType)asset_type;
-}
-
-// static
-const std::string &LLFolderType::badLookup()
-{
- static const std::string sBadLookup = "llfoldertype_bad_lookup";
- return sBadLookup;
-}
diff --git a/indra/llcommon/llfoldertype.h b/indra/llcommon/llfoldertype.h
deleted file mode 100644
index 7aa77f7f7e..0000000000
--- a/indra/llcommon/llfoldertype.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/**
- * @file llfoldertype.h
- * @brief Declaration of LLFolderType.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLFOLDERTYPE_H
-#define LL_LLFOLDERTYPE_H
-
-#include <string>
-#include "llassettype.h"
-
-// This class handles folder types (similar to assettype, except for folders)
-// and operations on those.
-class LL_COMMON_API LLFolderType
-{
-public:
- // ! BACKWARDS COMPATIBILITY ! Folder type enums must match asset type enums.
- enum EType
- {
- FT_TEXTURE = 0,
-
- FT_SOUND = 1,
-
- FT_CALLINGCARD = 2,
-
- FT_LANDMARK = 3,
-
- FT_CLOTHING = 5,
-
- FT_OBJECT = 6,
-
- FT_NOTECARD = 7,
-
- FT_ROOT_INVENTORY = 8,
- // We'd really like to change this to 9 since AT_CATEGORY is 8,
- // but "My Inventory" has been type 8 for a long time.
-
- FT_LSL_TEXT = 10,
-
- FT_BODYPART = 13,
-
- FT_TRASH = 14,
-
- FT_SNAPSHOT_CATEGORY = 15,
-
- FT_LOST_AND_FOUND = 16,
-
- FT_ANIMATION = 20,
-
- FT_GESTURE = 21,
-
- FT_FAVORITE = 23,
-
- FT_ENSEMBLE_START = 26,
- FT_ENSEMBLE_END = 45,
- // This range is reserved for special clothing folder types.
-
- FT_CURRENT_OUTFIT = 46,
- FT_OUTFIT = 47,
- FT_MY_OUTFITS = 48,
-
- FT_INBOX = 49,
-
- FT_COUNT = 50,
-
- FT_NONE = -1
- };
-
- static EType lookup(const std::string& type_name);
- static const std::string& lookup(EType folder_type);
-
- static bool lookupIsProtectedType(EType folder_type);
- static bool lookupIsEnsembleType(EType folder_type);
-
- static LLAssetType::EType folderTypeToAssetType(LLFolderType::EType folder_type);
- static LLFolderType::EType assetTypeToFolderType(LLAssetType::EType asset_type);
-
- static const std::string& badLookup(); // error string when a lookup fails
-
-protected:
- LLFolderType() {}
- ~LLFolderType() {}
-};
-
-#endif // LL_LLFOLDERTYPE_H
diff --git a/indra/llcommon/llformat.cpp b/indra/llcommon/llformat.cpp
index cf509bee14..3b2b3038ea 100644..100755
--- a/indra/llcommon/llformat.cpp
+++ b/indra/llcommon/llformat.cpp
@@ -3,31 +3,25 @@
* @date January 2007
* @brief string formatting utility
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,16 +31,40 @@
#include <cstdarg>
-std::string llformat(const char *fmt, ...)
+// common used function with va_list argument
+// wrapper for vsnprintf to be called from llformatXXX functions.
+static void va_format(std::string& out, const char *fmt, va_list va)
{
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
+ out.assign(tstr);
+}
+
+std::string llformat(const char *fmt, ...)
+{
+ std::string res;
+ va_list va;
+ va_start(va, fmt);
+ va_format(res, fmt, va);
+ va_end(va);
+ return res;
+}
+
+std::string llformat_to_utf8(const char *fmt, ...)
+{
+ std::string res;
+ va_list va;
+ va_start(va, fmt);
+ va_format(res, fmt, va);
va_end(va);
- return std::string(tstr);
+
+#if LL_WINDOWS
+ // made converting to utf8. See EXT-8318.
+ res = ll_convert_string_to_utf8_string(res);
+#endif
+ return res;
}
diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h
index dc64edb26d..fb8e7cd045 100644..100755
--- a/indra/llcommon/llformat.h
+++ b/indra/llcommon/llformat.h
@@ -3,31 +3,25 @@
* @date January 2007
* @brief string formatting utility
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,11 +29,15 @@
#define LL_LLFORMAT_H
// Use as follows:
-// llinfos << llformat("Test:%d (%.2f %.2f)", idx, x, y) << llendl;
+// LL_INFOS() << llformat("Test:%d (%.2f %.2f)", idx, x, y) << LL_ENDL;
//
// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun)
// should perhaps be replaced with boost::format.
std::string LL_COMMON_API llformat(const char *fmt, ...);
+// the same version as above but ensures that returned string is in utf8 on windows
+// to enable correct converting utf8_to_wstring.
+std::string LL_COMMON_API llformat_to_utf8(const char *fmt, ...);
+
#endif // LL_LLFORMAT_H
diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp
index 138c67a369..1e9920746b 100644..100755
--- a/indra/llcommon/llframetimer.cpp
+++ b/indra/llcommon/llframetimer.cpp
@@ -1,31 +1,25 @@
/**
* @file llframetimer.cpp
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,7 +37,6 @@ 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
@@ -121,10 +114,10 @@ F64 LLFrameTimer::expiresAt() const
BOOL LLFrameTimer::checkExpirationAndReset(F32 expiration)
{
- //llinfos << "LLFrameTimer::checkExpirationAndReset()" << llendl;
- //llinfos << " mStartTime:" << mStartTime << llendl;
- //llinfos << " sFrameTime:" << sFrameTime << llendl;
- //llinfos << " mExpiry: " << mExpiry << llendl;
+ //LL_INFOS() << "LLFrameTimer::checkExpirationAndReset()" << LL_ENDL;
+ //LL_INFOS() << " mStartTime:" << mStartTime << LL_ENDL;
+ //LL_INFOS() << " sFrameTime:" << sFrameTime << LL_ENDL;
+ //LL_INFOS() << " mExpiry: " << mExpiry << LL_ENDL;
if(hasExpired())
{
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h
index be2d9b0703..81bd5da8a3 100644..100755
--- a/indra/llcommon/llframetimer.h
+++ b/indra/llcommon/llframetimer.h
@@ -3,31 +3,25 @@
* @brief A lightweight timer that measures seconds and is only
* updated once per frame.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -41,7 +35,6 @@
*/
#include "lltimer.h"
-#include "timing.h"
class LL_COMMON_API LLFrameTimer
{
@@ -50,7 +43,7 @@ public:
// Return the number of seconds since the start of this
// application instance.
- static F64 getElapsedSeconds()
+ static F64SecondsImplicit getElapsedSeconds()
{
// Loses msec precision after ~4.5 hours...
return sFrameTime;
@@ -59,7 +52,7 @@ public:
// Return a low precision usec since epoch
static U64 getTotalTime()
{
- return sTotalTime ? sTotalTime : totalTime();
+ return sTotalTime ? U64MicrosecondsImplicit(sTotalTime) : totalTime();
}
// Return a low precision seconds since epoch
diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h
new file mode 100755
index 0000000000..401e4d759a
--- /dev/null
+++ b/indra/llcommon/llhandle.h
@@ -0,0 +1,216 @@
+/**
+* @file llhandle.h
+* @brief "Handle" to an object (usually a floater) whose lifetime you don't
+* control.
+*
+* $LicenseInfo:firstyear=2001&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2010, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+#ifndef LLHANDLE_H
+#define LLHANDLE_H
+
+#include "llpointer.h"
+#include <boost/type_traits/is_convertible.hpp>
+#include <boost/utility/enable_if.hpp>
+
+/**
+ * Helper object for LLHandle. Don't instantiate these directly, used
+ * exclusively by LLHandle.
+ */
+class LLTombStone : public LLRefCount
+{
+public:
+ LLTombStone(void* target = NULL) : mTarget(target) {}
+
+ void setTarget(void* target) { mTarget = target; }
+ void* getTarget() const { return mTarget; }
+private:
+ mutable void* mTarget;
+};
+
+/**
+ * LLHandles are used to refer to objects whose lifetime you do not control or influence.
+ * Calling get() on a handle will return a pointer to the referenced object or NULL,
+ * if the object no longer exists. Note that during the lifetime of the returned pointer,
+ * you are assuming that the object will not be deleted by any action you perform,
+ * or any other thread, as normal when using pointers, so avoid using that pointer outside of
+ * the local code block.
+ *
+ * https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669
+ *
+ * The implementation is like some "weak pointer" implementations. When we
+ * can't control the lifespan of the referenced object of interest, we can
+ * still instantiate a proxy object whose lifespan we DO control, and store in
+ * the proxy object a dumb pointer to the actual target. Then we just have to
+ * ensure that on destruction of the target object, the proxy's dumb pointer
+ * is set NULL.
+ *
+ * LLTombStone is our proxy object. LLHandle contains an LLPointer to the
+ * LLTombStone, so every copy of an LLHandle increments the LLTombStone's ref
+ * count as usual.
+ *
+ * One copy of the LLHandle, specifically the LLRootHandle, must be stored in
+ * the referenced object. Destroying the LLRootHandle is what NULLs the
+ * proxy's target pointer.
+ *
+ * Minor optimization: we want LLHandle's mTombStone to always be a valid
+ * LLPointer, saving some conditionals in dereferencing. That's the
+ * getDefaultTombStone() mechanism. The default LLTombStone object's target
+ * pointer is always NULL, so it's semantically identical to allowing
+ * mTombStone to be invalid.
+ */
+template <typename T>
+class LLHandle
+{
+ template <typename U> friend class LLHandle;
+ template <typename U> friend class LLHandleProvider;
+public:
+ LLHandle() : mTombStone(getDefaultTombStone()) {}
+
+ template<typename U>
+ LLHandle(const LLHandle<U>& other, typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0)
+ : mTombStone(other.mTombStone)
+ {}
+
+ bool isDead() const
+ {
+ return mTombStone->getTarget() == NULL;
+ }
+
+ void markDead()
+ {
+ mTombStone = getDefaultTombStone();
+ }
+
+ T* get() const
+ {
+ return reinterpret_cast<T*>(mTombStone->getTarget());
+ }
+
+ friend bool operator== (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return lhs.mTombStone == rhs.mTombStone;
+ }
+ friend bool operator!= (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return !(lhs == rhs);
+ }
+ friend bool operator< (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return lhs.mTombStone < rhs.mTombStone;
+ }
+ friend bool operator> (const LLHandle<T>& lhs, const LLHandle<T>& rhs)
+ {
+ return lhs.mTombStone > rhs.mTombStone;
+ }
+
+protected:
+ LLPointer<LLTombStone> mTombStone;
+
+private:
+ typedef T* pointer_t;
+ static LLPointer<LLTombStone>& getDefaultTombStone()
+ {
+ static LLPointer<LLTombStone> sDefaultTombStone = new LLTombStone;
+ return sDefaultTombStone;
+ }
+};
+
+/**
+ * LLRootHandle isa LLHandle which must be stored in the referenced object.
+ * You can either store it directly and explicitly bind(this), or derive from
+ * LLHandleProvider (q.v.) which automates that for you. The essential point
+ * is that destroying the LLRootHandle (as a consequence of destroying the
+ * referenced object) calls unbind(), setting the LLTombStone's target pointer
+ * NULL.
+ */
+template <typename T>
+class LLRootHandle : public LLHandle<T>
+{
+public:
+ typedef LLRootHandle<T> self_t;
+ typedef LLHandle<T> base_t;
+
+ LLRootHandle(T* object) { bind(object); }
+ LLRootHandle() {};
+ ~LLRootHandle() { unbind(); }
+
+ // this is redundant, since an LLRootHandle *is* an LLHandle
+ //LLHandle<T> getHandle() { return LLHandle<T>(*this); }
+
+ void bind(T* object)
+ {
+ // unbind existing tombstone
+ if (LLHandle<T>::mTombStone.notNull())
+ {
+ if (LLHandle<T>::mTombStone->getTarget() == (void*)object) return;
+ LLHandle<T>::mTombStone->setTarget(NULL);
+ }
+ // tombstone reference counted, so no paired delete
+ LLHandle<T>::mTombStone = new LLTombStone((void*)object);
+ }
+
+ void unbind()
+ {
+ LLHandle<T>::mTombStone->setTarget(NULL);
+ }
+
+ //don't allow copying of root handles, since there should only be one
+private:
+ LLRootHandle(const LLRootHandle& other) {};
+};
+
+/**
+ * Use this as a mixin for simple classes that need handles and when you don't
+ * want handles at multiple points of the inheritance hierarchy
+ */
+template <typename T>
+class LLHandleProvider
+{
+public:
+ LLHandle<T> getHandle() const
+ {
+ // perform lazy binding to avoid small tombstone allocations for handle
+ // providers whose handles are never referenced
+ mHandle.bind(static_cast<T*>(const_cast<LLHandleProvider<T>* >(this)));
+ return mHandle;
+ }
+
+ template <typename U>
+ LLHandle<U> getDerivedHandle(typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) const
+ {
+ LLHandle<U> downcast_handle;
+ downcast_handle.mTombStone = getHandle().mTombStone;
+ return downcast_handle;
+ }
+
+protected:
+ typedef LLHandle<T> handle_type_t;
+ LLHandleProvider()
+ {
+ // provided here to enforce T deriving from LLHandleProvider<T>
+ }
+
+private:
+ mutable LLRootHandle<T> mHandle;
+};
+
+#endif
diff --git a/indra/llcommon/llhash.h b/indra/llcommon/llhash.h
index 0ce10883a4..4b58e81565 100644..100755
--- a/indra/llcommon/llhash.h
+++ b/indra/llcommon/llhash.h
@@ -2,57 +2,32 @@
* @file llhash.h
* @brief Wrapper for a hash function.
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#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 >= 40300 // gcc 4.3 and up
-# include <backward/hashtable.h>
-# elif 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
-#elif LL_SOLARIS
-#include <ext/hashtable.h>
-#else
-#error Please define your platform.
-#endif
+#include <boost/functional/hash.hpp>
// Warning - an earlier template-based version of this routine did not do
// the correct thing on Windows. Since this is only used to get
@@ -61,17 +36,17 @@
inline size_t llhash( const char * value )
{
-#if LL_WINDOWS
- return stdext::hash_value(value);
-#elif ( (defined _STLPORT_VERSION) || ((LL_LINUX) && (__GNUC__ <= 2)) )
- std::hash<const char *> H;
- return H(value);
-#elif LL_DARWIN || LL_LINUX || LL_SOLARIS
- __gnu_cxx::hash<const char *> H;
- return H(value);
-#else
-#error Please define your platform.
-#endif
+ // boost::hash is defined for std::string and for char, but there's no
+ // special overload for const char*. The lazy approach would be to
+ // instantiate a std::string and take its hash, but that might be more
+ // overhead than our callers want. Or we could use boost::hash_range() --
+ // but that would require a preliminary pass over the value to determine
+ // the end iterator. Instead, use boost::hash_combine() to hash individual
+ // characters.
+ std::size_t seed = 0;
+ for ( ; *value; ++value)
+ boost::hash_combine(seed, *value);
+ return seed;
}
#endif
diff --git a/indra/llcommon/llheartbeat.cpp b/indra/llcommon/llheartbeat.cpp
index 49c0ad508b..19b7452748 100644..100755
--- a/indra/llcommon/llheartbeat.cpp
+++ b/indra/llcommon/llheartbeat.cpp
@@ -2,31 +2,25 @@
* @file llheartbeat.cpp
* @brief Class encapsulating logic for telling a watchdog that we live.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -104,7 +98,7 @@ LLHeartbeat::rawSendWithTimeout(F32 timeout_sec)
mTimeoutTimer.setTimerExpirySec(timeout_sec);
do {
result = rawSend();
- //llinfos << " HEARTSENDc=" << result << llendl;
+ //LL_INFOS() << " HEARTSENDc=" << result << LL_ENDL;
} while (result==1 && !mTimeoutTimer.hasExpired());
return result;
@@ -124,7 +118,7 @@ LLHeartbeat::send(F32 timeout_sec)
// zero-timeout; we don't care too much whether our
// heartbeat was digested.
result = rawSend();
- //llinfos << " HEARTSENDb=" << result << llendl;
+ //LL_INFOS() << " HEARTSENDb=" << result << LL_ENDL;
}
}
@@ -152,14 +146,14 @@ LLHeartbeat::send(F32 timeout_sec)
// It's been ages since we successfully had a heartbeat
// digested by the watchdog. Sit here and spin a while
// in the hope that we can force it through.
- llwarns << "Unable to deliver heartbeat to launcher for " << mPanicTimer.getElapsedTimeF32() << " seconds. Going to try very hard for up to " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << llendl;
+ LL_WARNS() << "Unable to deliver heartbeat to launcher for " << mPanicTimer.getElapsedTimeF32() << " seconds. Going to try very hard for up to " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL;
result = rawSendWithTimeout(mAggressiveHeartbeatMaxBlockingSecs);
if (result == 0) {
total_success = true;
} else {
// we couldn't even force it through. That's bad,
// but we'll try again in a while.
- llwarns << "Could not deliver heartbeat to launcher even after trying very hard for " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << llendl;
+ LL_WARNS() << "Could not deliver heartbeat to launcher even after trying very hard for " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL;
}
// in any case, reset the panic timer.
diff --git a/indra/llcommon/llheartbeat.h b/indra/llcommon/llheartbeat.h
index 6f7026970f..4a75fcc103 100644..100755
--- a/indra/llcommon/llheartbeat.h
+++ b/indra/llcommon/llheartbeat.h
@@ -2,31 +2,25 @@
* @file llheartbeat.h
* @brief Class encapsulating logic for telling a watchdog that we live.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llhttpstatuscodes.h b/indra/llcommon/llhttpstatuscodes.h
deleted file mode 100644
index 83dde17d90..0000000000
--- a/indra/llcommon/llhttpstatuscodes.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/**
- * @file llhttpstatuscodes.h
- * @brief Constants for HTTP status codes
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_HTTP_STATUS_CODES_H
-#define LL_HTTP_STATUS_CODES_H
-
-#include "stdtypes.h"
-
-// Standard errors from HTTP spec:
-// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1
-const S32 HTTP_CONTINUE = 100;
-const S32 HTTP_SWITCHING_PROTOCOLS = 101;
-
-// Success
-const S32 HTTP_OK = 200;
-const S32 HTTP_CREATED = 201;
-const S32 HTTP_ACCEPTED = 202;
-const S32 HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
-const S32 HTTP_NO_CONTENT = 204;
-const S32 HTTP_RESET_CONTENT = 205;
-const S32 HTTP_PARTIAL_CONTENT = 206;
-
-// Redirection
-const S32 HTTP_MULTIPLE_CHOICES = 300;
-const S32 HTTP_MOVED_PERMANENTLY = 301;
-const S32 HTTP_FOUND = 302;
-const S32 HTTP_SEE_OTHER = 303;
-const S32 HTTP_NOT_MODIFIED = 304;
-const S32 HTTP_USE_PROXY = 305;
-const S32 HTTP_TEMPORARY_REDIRECT = 307;
-
-// Client Error
-const S32 HTTP_BAD_REQUEST = 400;
-const S32 HTTP_UNAUTHORIZED = 401;
-const S32 HTTP_PAYMENT_REQUIRED = 402;
-const S32 HTTP_FORBIDDEN = 403;
-const S32 HTTP_NOT_FOUND = 404;
-const S32 HTTP_METHOD_NOT_ALLOWED = 405;
-const S32 HTTP_NOT_ACCEPTABLE = 406;
-const S32 HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
-const S32 HTTP_REQUEST_TIME_OUT = 408;
-const S32 HTTP_CONFLICT = 409;
-const S32 HTTP_GONE = 410;
-const S32 HTTP_LENGTH_REQUIRED = 411;
-const S32 HTTP_PRECONDITION_FAILED = 412;
-const S32 HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
-const S32 HTTP_REQUEST_URI_TOO_LARGE = 414;
-const S32 HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
-const S32 HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
-const S32 HTTP_EXPECTATION_FAILED = 417;
-
-// Server Error
-const S32 HTTP_INTERNAL_SERVER_ERROR = 500;
-const S32 HTTP_NOT_IMPLEMENTED = 501;
-const S32 HTTP_BAD_GATEWAY = 502;
-const S32 HTTP_SERVICE_UNAVAILABLE = 503;
-const S32 HTTP_GATEWAY_TIME_OUT = 504;
-const S32 HTTP_VERSION_NOT_SUPPORTED = 505;
-
-// We combine internal process errors with status codes
-// These status codes should not be sent over the wire
-// and indicate something went wrong internally.
-// If you get these they are not normal.
-const S32 HTTP_INTERNAL_ERROR = 499;
-
-#endif
diff --git a/indra/llcommon/llindexedqueue.h b/indra/llcommon/llindexedqueue.h
deleted file mode 100644
index 1b80e6eda4..0000000000
--- a/indra/llcommon/llindexedqueue.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/**
- * @file llindexedqueue.h
- * @brief An indexed FIFO queue, where only one element with each key
- * can be in the queue.
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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/llindexedvector.h b/indra/llcommon/llindexedvector.h
new file mode 100755
index 0000000000..68c3821802
--- /dev/null
+++ b/indra/llcommon/llindexedvector.h
@@ -0,0 +1,100 @@
+/**
+ * @file lldarray.h
+ * @brief Wrapped std::vector for backward compatibility.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLDARRAY_H
+#define LL_LLDARRAY_H
+
+#include "llerror.h"
+
+#include <vector>
+#include <map>
+
+//--------------------------------------------------------
+// LLIndexedVector
+//--------------------------------------------------------
+
+template <typename Type, typename Key, int BlockSize = 32>
+class LLIndexedVector
+{
+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:
+ LLIndexedVector() { 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.size(); }
+
+ Type& operator[](const Key& k)
+ {
+ typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k);
+ if (iter == mIndexMap.end())
+ {
+ U32 n = mVector.size();
+ mIndexMap[k] = n;
+ mVector.push_back(Type());
+ llassert(mVector.size() == mIndexMap.size());
+ return mVector[n];
+ }
+ else
+ {
+ return mVector[iter->second];
+ }
+ }
+
+ const_iterator find(const Key& k) const
+ {
+ typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k);
+ if(iter == mIndexMap.end())
+ {
+ return mVector.end();
+ }
+ else
+ {
+ return mVector.begin() + iter->second;
+ }
+ }
+};
+
+#endif
diff --git a/indra/llcommon/llinitparam.cpp b/indra/llcommon/llinitparam.cpp
new file mode 100755
index 0000000000..aa2f4eb289
--- /dev/null
+++ b/indra/llcommon/llinitparam.cpp
@@ -0,0 +1,487 @@
+/**
+ * @file llinitparam.cpp
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llinitparam.h"
+#include "llformat.h"
+
+
+namespace LLInitParam
+{
+
+ predicate_rule_t default_parse_rules()
+ {
+ return ll_make_predicate(PROVIDED) && !ll_make_predicate(EMPTY);
+ }
+
+ //
+ // Param
+ //
+ Param::Param(BaseBlock* enclosing_block)
+ : mIsProvided(false)
+ {
+ const U8* my_addr = reinterpret_cast<const U8*>(this);
+ const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
+ U32 enclosing_block_offset = 0x7FFFffff & (U32)(my_addr - block_addr);
+ mEnclosingBlockOffsetLow = enclosing_block_offset & 0x0000ffff;
+ mEnclosingBlockOffsetHigh = (enclosing_block_offset & 0x007f0000) >> 16;
+ }
+
+ //
+ // ParamDescriptor
+ //
+ ParamDescriptor::ParamDescriptor(param_handle_t p,
+ merge_func_t merge_func,
+ deserialize_func_t deserialize_func,
+ serialize_func_t serialize_func,
+ validation_func_t validation_func,
+ inspect_func_t inspect_func,
+ S32 min_count,
+ S32 max_count)
+ : mParamHandle(p),
+ mMergeFunc(merge_func),
+ mDeserializeFunc(deserialize_func),
+ mSerializeFunc(serialize_func),
+ mValidationFunc(validation_func),
+ mInspectFunc(inspect_func),
+ mMinCount(min_count),
+ mMaxCount(max_count),
+ mUserData(NULL)
+ {}
+
+ ParamDescriptor::ParamDescriptor()
+ : mParamHandle(0),
+ mMergeFunc(NULL),
+ mDeserializeFunc(NULL),
+ mSerializeFunc(NULL),
+ mValidationFunc(NULL),
+ mInspectFunc(NULL),
+ mMinCount(0),
+ mMaxCount(0),
+ mUserData(NULL)
+ {}
+
+ ParamDescriptor::~ParamDescriptor()
+ {
+ delete mUserData;
+ }
+
+ //
+ // Parser
+ //
+ Parser::~Parser()
+ {}
+
+ void Parser::parserWarning(const std::string& message)
+ {
+ if (mParseSilently) return;
+ LL_WARNS() << message << LL_ENDL;
+ }
+
+ void Parser::parserError(const std::string& message)
+ {
+ if (mParseSilently) return;
+ LL_ERRS() << message << LL_ENDL;
+ }
+
+
+ //
+ // BlockDescriptor
+ //
+ void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data)
+ {
+ mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end());
+ std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams));
+ std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList));
+ std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams));
+ }
+
+ void BlockDescriptor::addParam(const ParamDescriptorPtr in_param, const char* char_name)
+ {
+ // create a copy of the param descriptor in mAllParams
+ // so other data structures can store a pointer to it
+ mAllParams.push_back(in_param);
+ ParamDescriptorPtr param(mAllParams.back());
+
+ std::string name(char_name);
+ if ((size_t)param->mParamHandle > mMaxParamOffset)
+ {
+ LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL;
+ }
+
+ if (name.empty())
+ {
+ mUnnamedParams.push_back(param);
+ }
+ else
+ {
+ // don't use insert, since we want to overwrite existing entries
+ mNamedParams[name] = param;
+ }
+
+ if (param->mValidationFunc)
+ {
+ mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc));
+ }
+ }
+
+ BlockDescriptor::BlockDescriptor()
+ : mMaxParamOffset(0),
+ mInitializationState(UNINITIALIZED),
+ mCurrentBlockPtr(NULL)
+ {}
+
+ // called by each derived class in least to most derived order
+ void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
+ {
+ descriptor.mCurrentBlockPtr = this;
+ descriptor.mMaxParamOffset = block_size;
+
+ switch(descriptor.mInitializationState)
+ {
+ case BlockDescriptor::UNINITIALIZED:
+ // copy params from base class here
+ descriptor.aggregateBlockData(base_descriptor);
+
+ descriptor.mInitializationState = BlockDescriptor::INITIALIZING;
+ break;
+ case BlockDescriptor::INITIALIZING:
+ descriptor.mInitializationState = BlockDescriptor::INITIALIZED;
+ break;
+ case BlockDescriptor::INITIALIZED:
+ // nothing to do
+ break;
+ }
+ }
+
+ param_handle_t BaseBlock::getHandleFromParam(const Param* param) const
+ {
+ const U8* param_address = reinterpret_cast<const U8*>(param);
+ const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+ return (param_address - baseblock_address);
+ }
+
+ bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent)
+ {
+ Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end());
+ if (!deserializeBlock(p, range, true))
+ {
+ if (!silent)
+ {
+ p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str()));
+ }
+ return false;
+ }
+ return true;
+ }
+
+
+ bool BaseBlock::validateBlock(bool emit_errors) const
+ {
+ // only validate block when it hasn't already passed validation with current data
+ if (!mValidated)
+ {
+ const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
+ for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it)
+ {
+ const Param* param = getParamFromHandle(it->first);
+ if (!it->second(param))
+ {
+ if (emit_errors)
+ {
+ LL_WARNS() << "Invalid param \"" << getParamName(block_data, param) << "\"" << LL_ENDL;
+ }
+ return false;
+ }
+ }
+ mValidated = true;
+ }
+ return mValidated;
+ }
+
+ bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const LLInitParam::BaseBlock* diff_block) const
+ {
+ bool serialized = false;
+ if (!predicate_rule.check(ll_make_predicate(PROVIDED, isProvided())))
+ {
+ return false;
+ }
+ // named param is one like LLView::Params::follows
+ // unnamed param is like LLView::Params::rect - implicit
+ const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
+
+ for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin();
+ it != block_data.mUnnamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = (*it)->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::serialize_func_t serialize_func = (*it)->mSerializeFunc;
+ if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided())))
+ {
+ const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+ serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param);
+ }
+ }
+
+ for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
+ it != block_data.mNamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = it->second->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::serialize_func_t serialize_func = it->second->mSerializeFunc;
+ if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided())))
+ {
+ // Ensure this param has not already been serialized
+ // Prevents <rect> from being serialized as its own tag.
+ bool duplicate = false;
+ for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin();
+ it2 != block_data.mUnnamedParams.end();
+ ++it2)
+ {
+ if (param_handle == (*it2)->mParamHandle)
+ {
+ duplicate = true;
+ break;
+ }
+ }
+
+ //FIXME: for now, don't attempt to serialize values under synonyms, as current parsers
+ // don't know how to detect them
+ if (duplicate)
+ {
+ continue;
+ }
+
+ name_stack.push_back(std::make_pair(it->first, !duplicate));
+ const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+ serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param);
+ name_stack.pop_back();
+ }
+ }
+
+ if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY)))
+ {
+ serialized |= parser.writeValue(Flag(), name_stack);
+ }
+ // was anything serialized in this block?
+ return serialized;
+ }
+
+ bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const
+ {
+ // named param is one like LLView::Params::follows
+ // unnamed param is like LLView::Params::rect - implicit
+ const BlockDescriptor& block_data = mostDerivedBlockDescriptor();
+
+ for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin();
+ it != block_data.mUnnamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = (*it)->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::inspect_func_t inspect_func = (*it)->mInspectFunc;
+ if (inspect_func)
+ {
+ name_stack.push_back(std::make_pair("", true));
+ inspect_func(*param, parser, name_stack, (*it)->mMinCount, (*it)->mMaxCount);
+ name_stack.pop_back();
+ }
+ }
+
+ for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
+ it != block_data.mNamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = it->second->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc;
+ if (inspect_func)
+ {
+ // Ensure this param has not already been inspected
+ bool duplicate = false;
+ for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin();
+ it2 != block_data.mUnnamedParams.end();
+ ++it2)
+ {
+ if (param_handle == (*it2)->mParamHandle)
+ {
+ duplicate = true;
+ break;
+ }
+ }
+
+ name_stack.push_back(std::make_pair(it->first, !duplicate));
+ inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
+ name_stack.pop_back();
+ }
+ }
+
+ return true;
+ }
+
+ bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored)
+ {
+ BlockDescriptor& block_data = mostDerivedBlockDescriptor();
+ bool names_left = name_stack_range.first != name_stack_range.second;
+
+ bool new_name = names_left
+ ? name_stack_range.first->second
+ : true;
+
+ if (names_left)
+ {
+ const std::string& top_name = name_stack_range.first->first;
+
+ BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name);
+ if (found_it != block_data.mNamedParams.end())
+ {
+ // find pointer to member parameter from offset table
+ Param* paramp = getParamFromHandle(found_it->second->mParamHandle);
+ ParamDescriptor::deserialize_func_t deserialize_func = found_it->second->mDeserializeFunc;
+
+ Parser::name_stack_range_t new_name_stack(name_stack_range.first, name_stack_range.second);
+ ++new_name_stack.first;
+ if (deserialize_func(*paramp, p, new_name_stack, new_name))
+ {
+ // value is no longer new, we know about it now
+ name_stack_range.first->second = false;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ // try to parse unnamed parameters, in declaration order
+ for ( BlockDescriptor::param_list_t::iterator it = block_data.mUnnamedParams.begin();
+ it != block_data.mUnnamedParams.end();
+ ++it)
+ {
+ Param* paramp = getParamFromHandle((*it)->mParamHandle);
+ ParamDescriptor::deserialize_func_t deserialize_func = (*it)->mDeserializeFunc;
+
+ if (deserialize_func && deserialize_func(*paramp, p, name_stack_range, new_name))
+ {
+ return true;
+ }
+ }
+
+ // if no match, and no names left on stack, this is just an existence assertion of this block
+ // verify by calling readValue with NoParamValue type, an inherently unparseable type
+ if (!names_left)
+ {
+ Flag no_value;
+ return p.readValue(no_value);
+ }
+
+ return false;
+ }
+
+ void BaseBlock::addSynonym(Param& param, const std::string& synonym)
+ {
+ BlockDescriptor& block_data = mostDerivedBlockDescriptor();
+ if (block_data.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ param_handle_t handle = getHandleFromParam(&param);
+
+ // check for invalid derivation from a paramblock (i.e. without using
+ // Block<T, Base_Class>
+ if ((size_t)handle > block_data.mMaxParamOffset)
+ {
+ LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL;
+ }
+
+ ParamDescriptorPtr param_descriptor = findParamDescriptor(param);
+ if (param_descriptor)
+ {
+ if (synonym.empty())
+ {
+ block_data.mUnnamedParams.push_back(param_descriptor);
+ }
+ else
+ {
+ block_data.mNamedParams[synonym] = param_descriptor;
+ }
+ }
+ }
+ }
+
+ const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const
+ {
+ param_handle_t handle = getHandleFromParam(paramp);
+ for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it)
+ {
+ if (it->second->mParamHandle == handle)
+ {
+ return it->first;
+ }
+ }
+
+ return LLStringUtil::null;
+ }
+
+ ParamDescriptorPtr BaseBlock::findParamDescriptor(const Param& param)
+ {
+ param_handle_t handle = getHandleFromParam(&param);
+ BlockDescriptor& descriptor = mostDerivedBlockDescriptor();
+ BlockDescriptor::all_params_list_t::iterator end_it = descriptor.mAllParams.end();
+ for (BlockDescriptor::all_params_list_t::iterator it = descriptor.mAllParams.begin();
+ it != end_it;
+ ++it)
+ {
+ if ((*it)->mParamHandle == handle) return *it;
+ }
+ return ParamDescriptorPtr();
+ }
+
+ // take all provided params from other and apply to self
+ // NOTE: this requires that "other" is of the same derived type as this
+ bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite)
+ {
+ bool some_param_changed = false;
+ BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end();
+ for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin();
+ it != end_it;
+ ++it)
+ {
+ const Param* other_paramp = other.getParamFromHandle((*it)->mParamHandle);
+ ParamDescriptor::merge_func_t merge_func = (*it)->mMergeFunc;
+ if (merge_func)
+ {
+ Param* paramp = getParamFromHandle((*it)->mParamHandle);
+ llassert(paramp->getEnclosingBlockOffset() == (*it)->mParamHandle);
+ some_param_changed |= merge_func(*paramp, *other_paramp, overwrite);
+ }
+ }
+ return some_param_changed;
+ }
+}
diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h
new file mode 100755
index 0000000000..c65b05f610
--- /dev/null
+++ b/indra/llcommon/llinitparam.h
@@ -0,0 +1,2839 @@
+/**
+ * @file llinitparam.h
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPARAM_H
+#define LL_LLPARAM_H
+
+#include <vector>
+#include <list>
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/type_traits/is_convertible.hpp>
+#include <boost/type_traits/is_enum.hpp>
+#include <boost/unordered_map.hpp>
+
+#include "llerror.h"
+#include "llstl.h"
+#include "llpredicate.h"
+#include "llsd.h"
+
+namespace LLTypeTags
+{
+ template <typename INNER_TYPE, int _SORT_ORDER>
+ struct TypeTagBase
+ {
+ typedef void is_tag_t;
+ typedef INNER_TYPE inner_t;
+ static const int SORT_ORDER=_SORT_ORDER;
+ };
+
+ template <int VAL1, int VAL2>
+ struct GreaterThan
+ {
+ static const bool value = VAL1 > VAL2;
+ };
+
+ template<typename ITEM, typename REST, bool NEEDS_SWAP = GreaterThan<ITEM::SORT_ORDER, REST::SORT_ORDER>::value >
+ struct Swap
+ {
+ typedef typename ITEM::template Cons<REST>::value_t value_t;
+ };
+
+ template<typename ITEM, typename REST>
+ struct Swap<ITEM, REST, true>
+ {
+ typedef typename REST::template Cons<Swap<ITEM, typename REST::inner_t>::value_t>::value_t value_t;
+ };
+
+ template<typename T, typename SORTABLE = void>
+ struct IsSortable
+ {
+ static const bool value = false;
+ };
+
+ template<typename T>
+ struct IsSortable<T, typename T::is_tag_t>
+ {
+ static const bool value = true;
+ };
+
+ template<typename ITEM, typename REST, bool IS_REST_SORTABLE = IsSortable<REST>::value>
+ struct InsertInto
+ {
+ typedef typename ITEM::template Cons<REST>::value_t value_t;
+ };
+
+ template<typename ITEM, typename REST>
+ struct InsertInto <ITEM, REST, true>
+ {
+ typedef typename Swap<ITEM, REST>::value_t value_t;
+ };
+
+ template<typename T, bool SORTABLE = IsSortable<T>::value>
+ struct Sorted
+ {
+ typedef T value_t;
+ };
+
+ template<typename T>
+ struct Sorted <T, true>
+ {
+ typedef typename InsertInto<T, typename Sorted<typename T::inner_t>::value_t>::value_t value_t;
+ };
+}
+
+namespace LLInitParam
+{
+ // used to indicate no matching value to a given name when parsing
+ struct Flag{};
+
+ template<typename T> const T& defaultValue() { static T value; return value; }
+
+ // wraps comparison operator between any 2 values of the same type
+ // specialize to handle cases where equality isn't defined well, or at all
+ template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value >
+ struct ParamCompare
+ {
+ static bool equals(const T &a, const T &b)
+ {
+ return a == b;
+ }
+ };
+
+ // boost function types are not comparable
+ template<typename T>
+ struct ParamCompare<T, true>
+ {
+ static bool equals(const T&a, const T &b)
+ {
+ return false;
+ }
+ };
+
+ template<>
+ struct ParamCompare<LLSD, false>
+ {
+ static bool equals(const LLSD &a, const LLSD &b) { return false; }
+ };
+
+ template<>
+ struct ParamCompare<Flag, false>
+ {
+ static bool equals(const Flag& a, const Flag& b) { return false; }
+ };
+
+
+ // helper functions and classes
+ typedef ptrdiff_t param_handle_t;
+ struct IS_A_BLOCK {};
+ struct NOT_BLOCK {};
+
+ // these templates allow us to distinguish between template parameters
+ // that derive from BaseBlock and those that don't
+ template<typename T, typename BLOCK_IDENTIFIER = void>
+ struct IsBlock
+ {
+ typedef NOT_BLOCK value_t;
+ };
+
+ template<typename T>
+ struct IsBlock<T, typename T::baseblock_base_class_t>
+ {
+ typedef IS_A_BLOCK value_t;
+ };
+
+ // ParamValue class directly manages the wrapped value
+ // by holding on to a copy (scalar params)
+ // or deriving from it (blocks)
+ // has specializations for custom value behavior
+ // and "tag" values like Lazy and Atomic
+ template<typename T, typename VALUE_IS_BLOCK = typename IsBlock<T>::value_t>
+ class ParamValue
+ {
+ typedef ParamValue<T, VALUE_IS_BLOCK> self_t;
+
+ public:
+ typedef T default_value_t;
+ typedef T value_t;
+
+ ParamValue(): mValue() {}
+ ParamValue(const default_value_t& other) : mValue(other) {}
+
+ void setValue(const value_t& val)
+ {
+ mValue = val;
+ }
+
+ const value_t& getValue() const
+ {
+ return mValue;
+ }
+
+ T& getValue()
+ {
+ return mValue;
+ }
+
+ bool isValid() const { return true; }
+
+ protected:
+ T mValue;
+ };
+
+ template<typename T>
+ class ParamValue<T, IS_A_BLOCK>
+ : public T
+ {
+ typedef ParamValue<T, IS_A_BLOCK> self_t;
+ public:
+ typedef T default_value_t;
+ typedef T value_t;
+
+ ParamValue()
+ : T()
+ {}
+
+ ParamValue(const default_value_t& other)
+ : T(other)
+ {}
+
+ void setValue(const value_t& val)
+ {
+ *this = val;
+ }
+
+ const value_t& getValue() const
+ {
+ return *this;
+ }
+
+ T& getValue()
+ {
+ return *this;
+ }
+ };
+
+
+ // empty default implementation of key cache
+ // leverages empty base class optimization
+ template <typename T>
+ class TypeValues
+ : public ParamValue<typename LLTypeTags::Sorted<T>::value_t>
+ {
+ private:
+ struct Inaccessable{};
+ public:
+ typedef std::map<std::string, T> value_name_map_t;
+ typedef Inaccessable name_t;
+ typedef TypeValues<T> type_value_t;
+ typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
+ typedef typename param_value_t::value_t value_t;
+
+ TypeValues(const typename param_value_t::value_t& val)
+ : param_value_t(val)
+ {}
+
+ void setValueName(const std::string& key) {}
+ std::string getValueName() const { return ""; }
+ std::string calcValueName(const value_t& value) const { return ""; }
+ void clearValueName() const {}
+
+ static bool getValueFromName(const std::string& name, value_t& value)
+ {
+ return false;
+ }
+
+ static bool valueNamesExist()
+ {
+ return false;
+ }
+
+ static std::vector<std::string>* getPossibleValues()
+ {
+ return NULL;
+ }
+
+ void assignNamedValue(const Inaccessable& name)
+ {}
+
+ operator const value_t&() const
+ {
+ return param_value_t::getValue();
+ }
+
+ const value_t& operator()() const
+ {
+ return param_value_t::getValue();
+ }
+
+ static value_name_map_t* getValueNames() {return NULL;}
+ };
+
+ // helper class to implement name value lookups
+ // and caching of last used name
+ template <typename T, typename DERIVED_TYPE = TypeValues<T>, bool IS_SPECIALIZED = true >
+ class TypeValuesHelper
+ : public ParamValue<typename LLTypeTags::Sorted<T>::value_t>
+ {
+ typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t;
+ public:
+ typedef typename std::map<std::string, T> value_name_map_t;
+ typedef std::string name_t;
+ typedef self_t type_value_t;
+ typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
+ typedef typename param_value_t::value_t value_t;
+
+ TypeValuesHelper(const typename param_value_t::value_t& val)
+ : param_value_t(val)
+ {}
+
+ //TODO: cache key by index to save on param block size
+ void setValueName(const std::string& value_name)
+ {
+ mValueName = value_name;
+ }
+
+ std::string getValueName() const
+ {
+ return mValueName;
+ }
+
+ std::string calcValueName(const value_t& value) const
+ {
+ value_name_map_t* map = getValueNames();
+ for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end();
+ it != end_it;
+ ++it)
+ {
+ if (ParamCompare<T>::equals(it->second, value))
+ {
+ return it->first;
+ }
+ }
+
+ return "";
+ }
+
+ void clearValueName() const
+ {
+ mValueName.clear();
+ }
+
+ static bool getValueFromName(const std::string& name, value_t& value)
+ {
+ value_name_map_t* map = getValueNames();
+ typename value_name_map_t::iterator found_it = map->find(name);
+ if (found_it == map->end()) return false;
+
+ value = found_it->second;
+ return true;
+ }
+
+ static bool valueNamesExist()
+ {
+ return !getValueNames()->empty();
+ }
+
+ static value_name_map_t* getValueNames()
+ {
+ static value_name_map_t sMap;
+ static bool sInitialized = false;
+
+ if (!sInitialized)
+ {
+ sInitialized = true;
+ DERIVED_TYPE::declareValues();
+ }
+ return &sMap;
+ }
+
+ static std::vector<std::string>* getPossibleValues()
+ {
+ static std::vector<std::string> sValues;
+
+ value_name_map_t* map = getValueNames();
+ for (typename value_name_map_t::iterator it = map->begin(), end_it = map->end();
+ it != end_it;
+ ++it)
+ {
+ sValues.push_back(it->first);
+ }
+ return &sValues;
+ }
+
+ static void declare(const std::string& name, const value_t& value)
+ {
+ (*getValueNames())[name] = value;
+ }
+
+ void operator ()(const std::string& name)
+ {
+ *this = name;
+ }
+
+ void assignNamedValue(const std::string& name)
+ {
+ if (getValueFromName(name, param_value_t::getValue()))
+ {
+ setValueName(name);
+ }
+ }
+
+ operator const value_t&() const
+ {
+ return param_value_t::getValue();
+ }
+
+ const value_t& operator()() const
+ {
+ return param_value_t::getValue();
+ }
+
+ protected:
+ static void getName(const std::string& name, const value_t& value)
+ {}
+
+ mutable std::string mValueName;
+ };
+
+ // string types can support custom named values, but need
+ // to disambiguate in code between a string that is a named value
+ // and a string that is a name
+ template <typename DERIVED_TYPE>
+ class TypeValuesHelper<std::string, DERIVED_TYPE, true>
+ : public TypeValuesHelper<std::string, DERIVED_TYPE, false>
+ {
+ public:
+ typedef TypeValuesHelper<std::string, DERIVED_TYPE, true> self_t;
+ typedef TypeValuesHelper<std::string, DERIVED_TYPE, false> base_t;
+ typedef std::string value_t;
+ typedef std::string name_t;
+ typedef self_t type_value_t;
+
+ TypeValuesHelper(const std::string& val)
+ : base_t(val)
+ {}
+
+ void operator ()(const std::string& name)
+ {
+ *this = name;
+ }
+
+ self_t& operator =(const std::string& name)
+ {
+ if (base_t::getValueFromName(name, ParamValue<std::string>::getValue()))
+ {
+ base_t::setValueName(name);
+ }
+ else
+ {
+ ParamValue<std::string>::setValue(name);
+ }
+ return *this;
+ }
+
+ operator const value_t&() const
+ {
+ return ParamValue<std::string>::getValue();
+ }
+
+ const value_t& operator()() const
+ {
+ return ParamValue<std::string>::getValue();
+ }
+
+ };
+
+ // parser base class with mechanisms for registering readers/writers/inspectors of different types
+ class LL_COMMON_API Parser
+ {
+ LOG_CLASS(Parser);
+ public:
+ typedef std::vector<std::pair<std::string, bool> > name_stack_t;
+ typedef std::pair<name_stack_t::iterator, name_stack_t::iterator> name_stack_range_t;
+ typedef std::vector<std::string> possible_values_t;
+
+ typedef bool (*parser_read_func_t)(Parser& parser, void* output);
+ typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&);
+ typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
+
+ typedef std::map<const std::type_info*, parser_read_func_t> parser_read_func_map_t;
+ typedef std::map<const std::type_info*, parser_write_func_t> parser_write_func_map_t;
+ typedef std::map<const std::type_info*, parser_inspect_func_t> parser_inspect_func_map_t;
+
+ public:
+
+ Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map)
+ : mParseSilently(false),
+ mParserReadFuncs(&read_map),
+ mParserWriteFuncs(&write_map),
+ mParserInspectFuncs(&inspect_map)
+ {}
+
+ virtual ~Parser();
+
+ template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0)
+ {
+ parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
+ if (found_it != mParserReadFuncs->end())
+ {
+ return found_it->second(*this, (void*)&param);
+ }
+
+ return false;
+ }
+
+ template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0)
+ {
+ parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T));
+ if (found_it != mParserReadFuncs->end())
+ {
+ return found_it->second(*this, (void*)&param);
+ }
+ else
+ {
+ found_it = mParserReadFuncs->find(&typeid(S32));
+ if (found_it != mParserReadFuncs->end())
+ {
+ S32 int_value;
+ bool parsed = found_it->second(*this, (void*)&int_value);
+ param = (T)int_value;
+ return parsed;
+ }
+ }
+ return false;
+ }
+
+ template <typename T> bool writeValue(const T& param, name_stack_t& name_stack)
+ {
+ parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T));
+ if (found_it != mParserWriteFuncs->end())
+ {
+ return found_it->second(*this, (const void*)&param, name_stack);
+ }
+ return false;
+ }
+
+ // dispatch inspection to registered inspection functions, for each parameter in a param block
+ template <typename T> bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values)
+ {
+ parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T));
+ if (found_it != mParserInspectFuncs->end())
+ {
+ found_it->second(name_stack, min_count, max_count, possible_values);
+ return true;
+ }
+ return false;
+ }
+
+ virtual std::string getCurrentElementName() = 0;
+ virtual void parserWarning(const std::string& message);
+ virtual void parserError(const std::string& message);
+ void setParseSilently(bool silent) { mParseSilently = silent; }
+
+ protected:
+ template <typename T>
+ void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL)
+ {
+ mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func));
+ mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func));
+ }
+
+ template <typename T>
+ void registerInspectFunc(parser_inspect_func_t inspect_func)
+ {
+ mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func));
+ }
+
+ bool mParseSilently;
+
+ private:
+ parser_read_func_map_t* mParserReadFuncs;
+ parser_write_func_map_t* mParserWriteFuncs;
+ parser_inspect_func_map_t* mParserInspectFuncs;
+ };
+
+ class Param;
+
+ enum ESerializePredicates
+ {
+ PROVIDED,
+ REQUIRED,
+ VALID,
+ HAS_DEFAULT_VALUE,
+ EMPTY
+ };
+
+ typedef LLPredicate::Rule<ESerializePredicates> predicate_rule_t;
+
+ predicate_rule_t default_parse_rules();
+
+ // various callbacks and constraints associated with an individual param
+ struct LL_COMMON_API ParamDescriptor
+ {
+ struct UserData
+ {
+ virtual ~UserData() {}
+ };
+
+ typedef bool(*merge_func_t)(Param&, const Param&, bool);
+ typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool);
+ typedef bool(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const predicate_rule_t rules, const Param* diff_param);
+ typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
+ typedef bool(*validation_func_t)(const Param*);
+
+ ParamDescriptor(param_handle_t p,
+ merge_func_t merge_func,
+ deserialize_func_t deserialize_func,
+ serialize_func_t serialize_func,
+ validation_func_t validation_func,
+ inspect_func_t inspect_func,
+ S32 min_count,
+ S32 max_count);
+
+ ParamDescriptor();
+ ~ParamDescriptor();
+
+ param_handle_t mParamHandle;
+ merge_func_t mMergeFunc;
+ deserialize_func_t mDeserializeFunc;
+ serialize_func_t mSerializeFunc;
+ inspect_func_t mInspectFunc;
+ validation_func_t mValidationFunc;
+ S32 mMinCount;
+ S32 mMaxCount;
+ S32 mNumRefs;
+ UserData* mUserData;
+ };
+
+ typedef boost::shared_ptr<ParamDescriptor> ParamDescriptorPtr;
+
+ // each derived Block class keeps a static data structure maintaining offsets to various params
+ class LL_COMMON_API BlockDescriptor
+ {
+ public:
+ BlockDescriptor();
+
+ typedef enum e_initialization_state
+ {
+ UNINITIALIZED,
+ INITIALIZING,
+ INITIALIZED
+ } EInitializationState;
+
+ void aggregateBlockData(BlockDescriptor& src_block_data);
+ void addParam(ParamDescriptorPtr param, const char* name);
+
+ typedef boost::unordered_map<const std::string, ParamDescriptorPtr> param_map_t;
+ typedef std::vector<ParamDescriptorPtr> param_list_t;
+ typedef std::list<ParamDescriptorPtr> all_params_list_t;
+ typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t;
+
+ param_map_t mNamedParams; // parameters with associated names
+ param_list_t mUnnamedParams; // parameters with_out_ associated names
+ param_validation_list_t mValidationList; // parameters that must be validated
+ all_params_list_t mAllParams; // all parameters, owns descriptors
+ size_t mMaxParamOffset;
+ EInitializationState mInitializationState; // whether or not static block data has been initialized
+ class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed
+ };
+
+ //TODO: implement in terms of owned_ptr
+ template<typename T>
+ class LazyValue
+ {
+ public:
+ LazyValue()
+ : mPtr(NULL)
+ {}
+
+ ~LazyValue()
+ {
+ delete mPtr;
+ }
+
+ LazyValue(const T& value)
+ {
+ mPtr = new T(value);
+ }
+
+ LazyValue(const LazyValue& other)
+ : mPtr(NULL)
+ {
+ *this = other;
+ }
+
+ LazyValue& operator = (const LazyValue& other)
+ {
+ if (!other.mPtr)
+ {
+ delete mPtr;
+ mPtr = NULL;
+ }
+ else
+ {
+ if (!mPtr)
+ {
+ mPtr = new T(*other.mPtr);
+ }
+ else
+ {
+ *mPtr = *(other.mPtr);
+ }
+ }
+ return *this;
+ }
+
+ bool operator==(const LazyValue& other) const
+ {
+ if (empty() || other.empty()) return false;
+ return *mPtr == *other.mPtr;
+ }
+
+ bool empty() const
+ {
+ return mPtr == NULL;
+ }
+
+ void set(const T& other)
+ {
+ if (!mPtr)
+ {
+ mPtr = new T(other);
+ }
+ else
+ {
+ *mPtr = other;
+ }
+ }
+
+ const T& get() const
+ {
+ return *ensureInstance();
+ }
+
+ T& get()
+ {
+ return *ensureInstance();
+ }
+
+ operator const T&() const
+ {
+ return get();
+ }
+
+ private:
+ // lazily allocate an instance of T
+ T* ensureInstance() const
+ {
+ if (mPtr == NULL)
+ {
+ mPtr = new T();
+ }
+ return mPtr;
+ }
+
+ private:
+
+ mutable T* mPtr;
+ };
+
+ // root class of all parameter blocks
+
+ class LL_COMMON_API BaseBlock
+ {
+ public:
+ // lift block tags into baseblock namespace so derived classes do not need to qualify them
+ typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK;
+ typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK;
+
+ template<typename T>
+ struct Sequential : public LLTypeTags::TypeTagBase<T, 2>
+ {
+ template <typename S> struct Cons { typedef Sequential<ParamValue<S> > value_t; };
+ template <typename S> struct Cons<Sequential<S> > { typedef Sequential<S> value_t; };
+ };
+
+ template<typename T>
+ struct Atomic : public LLTypeTags::TypeTagBase<T, 1>
+ {
+ template <typename S> struct Cons { typedef Atomic<ParamValue<S> > value_t; };
+ template <typename S> struct Cons<Atomic<S> > { typedef Atomic<S> value_t; };
+ };
+
+ template<typename T, typename BLOCK_T = typename IsBlock<T>::value_t >
+ struct Lazy : public LLTypeTags::TypeTagBase<T, 0>
+ {
+ template <typename S> struct Cons
+ {
+ typedef Lazy<ParamValue<S, BLOCK_T>, BLOCK_T> value_t;
+ };
+ template <typename S> struct Cons<Lazy<S, IS_A_BLOCK> >
+ {
+ typedef Lazy<S, IS_A_BLOCK> value_t;
+ };
+ template <typename S> struct Cons<Lazy<S, NOT_A_BLOCK> >
+ {
+ typedef Lazy<S, BLOCK_T> value_t;
+ };
+ };
+
+ // "Multiple" constraint types, put here in root class to avoid ambiguity during use
+ struct AnyAmount
+ {
+ enum { minCount = 0 };
+ enum { maxCount = U32_MAX };
+ };
+
+ template<U32 MIN_AMOUNT>
+ struct AtLeast
+ {
+ enum { minCount = MIN_AMOUNT };
+ enum { maxCount = U32_MAX };
+ };
+
+ template<U32 MAX_AMOUNT>
+ struct AtMost
+ {
+ enum { minCount = 0 };
+ enum { maxCount = MAX_AMOUNT };
+ };
+
+ template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
+ struct Between
+ {
+ enum { minCount = MIN_AMOUNT };
+ enum { maxCount = MAX_AMOUNT };
+ };
+
+ template<U32 EXACT_COUNT>
+ struct Exactly
+ {
+ enum { minCount = EXACT_COUNT };
+ enum { maxCount = EXACT_COUNT };
+ };
+
+ // this typedef identifies derived classes as being blocks
+ typedef void baseblock_base_class_t;
+ LOG_CLASS(BaseBlock);
+ friend class Param;
+
+ BaseBlock()
+ : mValidated(false),
+ mParamProvided(false)
+ {}
+
+ virtual ~BaseBlock() {}
+ bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
+
+ param_handle_t getHandleFromParam(const Param* param) const;
+ bool validateBlock(bool emit_errors = true) const;
+
+ bool isProvided() const
+ {
+ return mParamProvided;
+ }
+
+ bool isValid() const
+ {
+ return validateBlock(false);
+ }
+
+
+ Param* getParamFromHandle(const param_handle_t param_handle)
+ {
+ if (param_handle == 0) return NULL;
+
+ U8* baseblock_address = reinterpret_cast<U8*>(this);
+ return reinterpret_cast<Param*>(baseblock_address + param_handle);
+ }
+
+ const Param* getParamFromHandle(const param_handle_t param_handle) const
+ {
+ const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+ return reinterpret_cast<const Param*>(baseblock_address + param_handle);
+ }
+
+ void addSynonym(Param& param, const std::string& synonym);
+
+ // Blocks can override this to do custom tracking of changes
+ virtual void paramChanged(const Param& changed_param, bool user_provided)
+ {
+ if (user_provided)
+ {
+ // a child param has been explicitly changed
+ // so *some* aspect of this block is now provided
+ mValidated = false;
+ mParamProvided = true;
+ }
+ }
+
+ bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
+ bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t rule, const BaseBlock* diff_block = NULL) const;
+ bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const;
+
+ virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
+ virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }
+
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const BaseBlock& other)
+ {
+ return false;
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const BaseBlock& other)
+ {
+ return false;
+ }
+
+ ParamDescriptorPtr findParamDescriptor(const Param& param);
+
+ // take all provided params from other and apply to self
+ bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite);
+
+ static BlockDescriptor& getBlockDescriptor()
+ {
+ static BlockDescriptor sBlockDescriptor;
+ return sBlockDescriptor;
+ }
+
+ protected:
+ void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);
+
+
+ bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
+ {
+ return mergeBlock(block_data, source, overwrite);
+ }
+
+ mutable bool mValidated; // lazy validation flag
+ bool mParamProvided;
+
+ private:
+ const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
+ };
+
+ class LL_COMMON_API Param
+ {
+ public:
+ void setProvided(bool is_provided = true)
+ {
+ mIsProvided = is_provided;
+ enclosingBlock().paramChanged(*this, is_provided);
+ }
+
+ Param& operator =(const Param& other)
+ {
+ mIsProvided = other.mIsProvided;
+ // don't change mEnclosingblockoffset
+ return *this;
+ }
+ protected:
+
+ bool anyProvided() const { return mIsProvided; }
+
+ Param(BaseBlock* enclosing_block);
+
+ // store pointer to enclosing block as offset to reduce space and allow for quick copying
+ BaseBlock& enclosingBlock() const
+ {
+ const U8* my_addr = reinterpret_cast<const U8*>(this);
+ // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class
+ return *const_cast<BaseBlock*>
+ (reinterpret_cast<const BaseBlock*>
+ (my_addr - (ptrdiff_t)getEnclosingBlockOffset()));
+ }
+
+ U32 getEnclosingBlockOffset() const
+ {
+ return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow;
+ }
+
+ private:
+ friend class BaseBlock;
+
+ //24 bits for member offset field and 1 bit for provided flag
+ U16 mEnclosingBlockOffsetLow;
+ U8 mEnclosingBlockOffsetHigh:7;
+ U8 mIsProvided:1;
+
+ };
+
+ template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ struct ParamIterator
+ {
+ typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::const_iterator const_iterator;
+ typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::iterator iterator;
+ };
+
+ // wrapper for parameter with a known type
+ // specialized to handle 4 cases:
+ // simple "scalar" value
+ // parameter that is itself a block
+ // multiple scalar values, stored in a vector
+ // multiple blocks, stored in a vector
+ template<typename T,
+ typename NAME_VALUE_LOOKUP = TypeValues<T>,
+ bool HAS_MULTIPLE_VALUES = false,
+ typename VALUE_IS_BLOCK = typename IsBlock<ParamValue<typename LLTypeTags::Sorted<T>::value_t> >::value_t>
+ class TypedParam
+ : public Param,
+ public NAME_VALUE_LOOKUP::type_value_t
+ {
+ protected:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t;
+ typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t;
+ typedef typename param_value_t::default_value_t default_value_t;
+ typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
+ public:
+ typedef typename param_value_t::value_t value_t;
+
+ using named_value_t::operator();
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ named_value_t(value)
+ {
+ if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
+ {
+ init(block_descriptor, validate_func, min_count, max_count, name);
+ }
+ }
+
+ bool isProvided() const { return Param::anyProvided(); }
+
+ bool isValid() const { return true; }
+
+ static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // no further names in stack, attempt to parse value now
+ if (name_stack_range.first == name_stack_range.second)
+ {
+ std::string name;
+
+ // try to parse a known named value
+ if(named_value_t::valueNamesExist()
+ && parser.readValue(name)
+ && named_value_t::getValueFromName(name, typed_param.getValue()))
+ {
+ typed_param.setValueName(name);
+ typed_param.setProvided();
+ return true;
+ }
+ // try to read value directly
+ else if (parser.readValue(typed_param.getValue()))
+ {
+ typed_param.clearValueName();
+ typed_param.setProvided();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
+ {
+ bool serialized = false;
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ const self_t* diff_typed_param = static_cast<const self_t*>(diff_param);
+
+ LLPredicate::Value<ESerializePredicates> predicate;
+ if (diff_typed_param && ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue()))
+ {
+ predicate.set(HAS_DEFAULT_VALUE);
+ }
+
+ predicate.set(VALID, typed_param.isValid());
+ predicate.set(PROVIDED, typed_param.anyProvided());
+ predicate.set(EMPTY, false);
+
+ if (!predicate_rule.check(predicate)) return false;
+
+ if (!name_stack.empty())
+ {
+ name_stack.back().second = true;
+ }
+
+ std::string key = typed_param.getValueName();
+
+ // first try to write out name of name/value pair
+
+ if (!key.empty())
+ {
+ if (!diff_typed_param || !ParamCompare<std::string>::equals(diff_typed_param->getValueName(), key))
+ {
+ serialized = parser.writeValue(key, name_stack);
+ }
+ }
+ // then try to serialize value directly
+ else if (!diff_typed_param || ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue()))
+ {
+ serialized = parser.writeValue(typed_param.getValue(), name_stack);
+ if (!serialized)
+ {
+ std::string calculated_key = typed_param.calcValueName(typed_param.getValue());
+ if (calculated_key.size()
+ && (!diff_typed_param
+ || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key)))
+ {
+ serialized = parser.writeValue(calculated_key, name_stack);
+ }
+ }
+ }
+ return serialized;
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // tell parser about our actual type
+ parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+ // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
+ if (named_value_t::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
+ }
+ }
+
+ void set(const value_t& val, bool flag_as_provided = true)
+ {
+ named_value_t::clearValueName();
+ named_value_t::setValue(val);
+ setProvided(flag_as_provided);
+ }
+
+ self_t& operator =(const typename named_value_t::name_t& name)
+ {
+ named_value_t::assignNamedValue(name);
+ return *this;
+ }
+
+ protected:
+
+ self_t& operator =(const self_t& other)
+ {
+ param_value_t::operator =(other);
+ Param::operator =(other);
+ return *this;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_typed_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ dst_typed_param.set(src_typed_param.getValue());
+ return true;
+ }
+ return false;
+ }
+ private:
+ void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
+ {
+ ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
+ block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count));
+ block_descriptor.addParam(param_descriptor, name);
+ }
+ };
+
+ // parameter that is a block
+ template <typename BLOCK_T, typename NAME_VALUE_LOOKUP>
+ class TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK>
+ : public Param,
+ public NAME_VALUE_LOOKUP::type_value_t
+ {
+ protected:
+ typedef ParamValue<typename LLTypeTags::Sorted<BLOCK_T>::value_t> param_value_t;
+ typedef typename param_value_t::default_value_t default_value_t;
+ typedef TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t;
+ typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
+ public:
+ using named_value_t::operator();
+ typedef typename param_value_t::value_t value_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ named_value_t(value)
+ {
+ if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
+ {
+ init(block_descriptor, validate_func, min_count, max_count, name);
+ }
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+
+ if (name_stack_range.first == name_stack_range.second)
+ { // try to parse a known named value
+ std::string name;
+
+ if(named_value_t::valueNamesExist()
+ && parser.readValue(name)
+ && named_value_t::getValueFromName(name, typed_param.getValue()))
+ {
+ typed_param.setValueName(name);
+ typed_param.setProvided();
+ return true;
+ }
+ }
+
+ if(typed_param.deserializeBlock(parser, name_stack_range, new_name))
+ { // attempt to parse block...
+ typed_param.clearValueName();
+ typed_param.setProvided();
+ return true;
+ }
+
+
+ return false;
+ }
+
+ static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+
+ LLPredicate::Value<ESerializePredicates> predicate;
+
+ predicate.set(VALID, typed_param.isValid());
+ predicate.set(PROVIDED, typed_param.anyProvided());
+
+ if (!predicate_rule.check(predicate)) return false;
+
+ if (!name_stack.empty())
+ {
+ name_stack.back().second = true;
+ }
+
+ std::string key = typed_param.getValueName();
+ if (!key.empty())
+ {
+ if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key))
+ {
+ parser.writeValue(key, name_stack);
+ return true;
+ }
+ }
+ else
+ {
+ return typed_param.serializeBlock(parser, name_stack, predicate_rule, static_cast<const self_t*>(diff_param));
+ }
+
+ return false;
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+
+ // tell parser about our actual type
+ parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL);
+ // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
+ if (named_value_t::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
+ }
+
+ typed_param.inspectBlock(parser, name_stack, min_count, max_count);
+ }
+
+ // a param-that-is-a-block is provided when the user has set one of its child params
+ // *and* the block as a whole validates
+ bool isProvided() const
+ {
+ return Param::anyProvided() && isValid();
+ }
+
+ bool isValid() const
+ {
+ return param_value_t::isValid();
+ }
+
+ // assign block contents to this param-that-is-a-block
+ void set(const value_t& val, bool flag_as_provided = true)
+ {
+ named_value_t::setValue(val);
+ named_value_t::clearValueName();
+ setProvided(flag_as_provided);
+ }
+
+ self_t& operator =(const typename named_value_t::name_t& name)
+ {
+ named_value_t::assignNamedValue(name);
+ return *this;
+ }
+
+ // propagate changed status up to enclosing block
+ /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided)
+ {
+ param_value_t::paramChanged(changed_param, user_provided);
+
+ if (user_provided)
+ {
+ setProvided();
+ named_value_t::clearValueName();
+ }
+ else
+ {
+ Param::enclosingBlock().paramChanged(*this, user_provided);
+ }
+ }
+
+ protected:
+
+ self_t& operator =(const self_t& other)
+ {
+ param_value_t::operator =(other);
+ Param::operator =(other);
+ return *this;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_typed_param.anyProvided())
+ {
+ if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::getBlockDescriptor(), src_typed_param, overwrite))
+ {
+ dst_typed_param.clearValueName();
+ dst_typed_param.setProvided(true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
+ {
+ ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
+ block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count));
+ block_descriptor.addParam(param_descriptor, name);
+ }
+ };
+
+ // list of non-block parameters
+ template <typename MULTI_VALUE_T, typename NAME_VALUE_LOOKUP>
+ class TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK>
+ : public Param
+ {
+ protected:
+ typedef TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t;
+ typedef ParamValue<typename LLTypeTags::Sorted<MULTI_VALUE_T>::value_t> param_value_t;
+ typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t;
+ typedef container_t default_value_t;
+ typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
+
+ public:
+ typedef typename param_value_t::value_t value_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mMinCount(min_count),
+ mMaxCount(max_count)
+ {
+ std::copy(value.begin(), value.end(), std::back_inserter(mValues));
+
+ if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
+ {
+ init(block_descriptor, validate_func, min_count, max_count, name);
+
+ }
+ }
+
+ bool isProvided() const { return Param::anyProvided() && isValid(); }
+
+ bool isValid() const
+ {
+ size_t num_elements = numValidElements();
+ return mMinCount < num_elements && num_elements < mMaxCount;
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ Parser::name_stack_range_t new_name_stack_range(name_stack_range);
+ self_t& typed_param = static_cast<self_t&>(param);
+ value_t value;
+
+ // pop first element if empty string
+ if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
+ {
+ ++new_name_stack_range.first;
+ }
+
+ // no further names in stack, attempt to parse value now
+ if (new_name_stack_range.first == new_name_stack_range.second)
+ {
+ std::string name;
+
+ // try to parse a known named value
+ if(named_value_t::valueNamesExist()
+ && parser.readValue(name)
+ && named_value_t::getValueFromName(name, value))
+ {
+ typed_param.add(value);
+ typed_param.mValues.back().setValueName(name);
+ return true;
+ }
+ else if (parser.readValue(value)) // attempt to read value directly
+ {
+ typed_param.add(value);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
+ {
+ bool serialized = false;
+ const self_t& typed_param = static_cast<const self_t&>(param);
+
+ LLPredicate::Value<ESerializePredicates> predicate;
+
+ predicate.set(REQUIRED, typed_param.mMinCount > 0);
+ predicate.set(VALID, typed_param.isValid());
+ predicate.set(PROVIDED, typed_param.anyProvided());
+ predicate.set(EMPTY, typed_param.mValues.empty());
+
+ if (!predicate_rule.check(predicate)) return false;
+
+ for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
+ it != end_it;
+ ++it)
+ {
+ std::string key = it->getValueName();
+ name_stack.push_back(std::make_pair(std::string(), true));
+
+ if(key.empty())
+ // not parsed via name values, write out value directly
+ {
+ bool value_written = parser.writeValue(*it, name_stack);
+ if (!value_written)
+ {
+ std::string calculated_key = it->calcValueName(it->getValue());
+ if (parser.writeValue(calculated_key, name_stack))
+ {
+ serialized = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ if(parser.writeValue(key, name_stack))
+ {
+ serialized = true;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ return serialized;
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ parser.inspectValue<MULTI_VALUE_T>(name_stack, min_count, max_count, NULL);
+ if (named_value_t::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
+ }
+ }
+
+ void set(const container_t& val, bool flag_as_provided = true)
+ {
+ mValues = val;
+ setProvided(flag_as_provided);
+ }
+
+ param_value_t& add()
+ {
+ mValues.push_back(value_t());
+ Param::setProvided();
+ return mValues.back();
+ }
+
+ self_t& add(const value_t& item)
+ {
+ mValues.push_back(item);
+ setProvided();
+ return *this;
+ }
+
+ self_t& add(const typename named_value_t::name_t& name)
+ {
+ value_t value;
+
+ // try to parse a per type named value
+ if (named_value_t::getValueFromName(name, value))
+ {
+ add(value);
+ mValues.back().setValueName(name);
+ }
+
+ return *this;
+ }
+
+ // implicit conversion
+ operator const container_t&() const { return mValues; }
+ // explicit conversion
+ const container_t& operator()() const { return mValues; }
+
+ typedef typename container_t::iterator iterator;
+ typedef typename container_t::const_iterator const_iterator;
+ iterator begin() { return mValues.begin(); }
+ iterator end() { return mValues.end(); }
+ const_iterator begin() const { return mValues.begin(); }
+ const_iterator end() const { return mValues.end(); }
+ bool empty() const { return mValues.empty(); }
+ size_t size() const { return mValues.size(); }
+
+ size_t numValidElements() const
+ {
+ return mValues.size();
+ }
+
+ protected:
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (overwrite)
+ {
+ std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues));
+ }
+ else
+ {
+ container_t new_values(src_typed_param.mValues);
+ std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values));
+ std::swap(dst_typed_param.mValues, new_values);
+ }
+
+ if (src_typed_param.begin() != src_typed_param.end())
+ {
+ dst_typed_param.setProvided();
+ }
+ return true;
+ }
+
+ container_t mValues;
+ size_t mMinCount,
+ mMaxCount;
+
+ private:
+ void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
+ {
+ ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
+ block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count));
+ block_descriptor.addParam(param_descriptor, name);
+ }
+ };
+
+ // list of block parameters
+ template <typename MULTI_BLOCK_T, typename NAME_VALUE_LOOKUP>
+ class TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK>
+ : public Param
+ {
+ protected:
+ typedef TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t;
+ typedef ParamValue<typename LLTypeTags::Sorted<MULTI_BLOCK_T>::value_t> param_value_t;
+ typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t;
+ typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t;
+ typedef container_t default_value_t;
+ typedef typename container_t::iterator iterator;
+ typedef typename container_t::const_iterator const_iterator;
+ public:
+ typedef typename param_value_t::value_t value_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mMinCount(min_count),
+ mMaxCount(max_count)
+ {
+ std::copy(value.begin(), value.end(), back_inserter(mValues));
+
+ if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
+ {
+ init(block_descriptor, validate_func, min_count, max_count, name);
+ }
+ }
+
+ bool isProvided() const { return Param::anyProvided() && isValid(); }
+
+ bool isValid() const
+ {
+ size_t num_elements = numValidElements();
+ return mMinCount < num_elements && num_elements < mMaxCount;
+ }
+
+
+ static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ Parser::name_stack_range_t new_name_stack_range(name_stack_range);
+ self_t& typed_param = static_cast<self_t&>(param);
+ bool new_value = false;
+ bool new_array_value = false;
+
+ // pop first element if empty string
+ if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty())
+ {
+ new_array_value = new_name_stack_range.first->second;
+ ++new_name_stack_range.first;
+ }
+
+ if (new_name || new_array_value || typed_param.mValues.empty())
+ {
+ new_value = true;
+ typed_param.mValues.push_back(value_t());
+ }
+ param_value_t& value = typed_param.mValues.back();
+
+ if (new_name_stack_range.first == new_name_stack_range.second)
+ { // try to parse a known named value
+ std::string name;
+
+ if(named_value_t::valueNamesExist()
+ && parser.readValue(name)
+ && named_value_t::getValueFromName(name, value.getValue()))
+ {
+ typed_param.mValues.back().setValueName(name);
+ typed_param.setProvided();
+ if (new_array_value)
+ {
+ name_stack_range.first->second = false;
+ }
+ return true;
+ }
+ }
+
+ // attempt to parse block...
+ if(value.deserializeBlock(parser, new_name_stack_range, new_name))
+ {
+ typed_param.setProvided();
+ if (new_array_value)
+ {
+ name_stack_range.first->second = false;
+ }
+ return true;
+ }
+
+
+ if (new_value)
+ { // failed to parse new value, pop it off
+ typed_param.mValues.pop_back();
+ }
+
+ return false;
+ }
+
+ static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param)
+ {
+ bool serialized = false;
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ LLPredicate::Value<ESerializePredicates> predicate;
+
+ predicate.set(REQUIRED, typed_param.mMinCount > 0);
+ predicate.set(VALID, typed_param.isValid());
+ predicate.set(PROVIDED, typed_param.anyProvided());
+ predicate.set(EMPTY, typed_param.mValues.empty());
+
+ if (!predicate_rule.check(predicate)) return false;
+
+ for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end();
+ it != end_it;
+ ++it)
+ {
+ name_stack.push_back(std::make_pair(std::string(), true));
+
+ std::string key = it->getValueName();
+ if (!key.empty())
+ {
+ serialized |= parser.writeValue(key, name_stack);
+ }
+ // Not parsed via named values, write out value directly
+ // NOTE: currently we don't do diffing of Multiples
+ else
+ {
+ serialized = it->serializeBlock(parser, name_stack, predicate_rule, NULL);
+ }
+
+ name_stack.pop_back();
+ }
+
+ if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY)))
+ {
+ serialized |= parser.writeValue(Flag(), name_stack);
+ }
+
+ return serialized;
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ const param_value_t& value_param = param_value_t(value_t());
+
+ // tell parser about our actual type
+ parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL);
+ // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
+ if (named_value_t::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues());
+ }
+
+ value_param.inspectBlock(parser, name_stack, min_count, max_count);
+ }
+
+ void set(const container_t& val, bool flag_as_provided = true)
+ {
+ mValues = val;
+ setProvided(flag_as_provided);
+ }
+
+ param_value_t& add()
+ {
+ mValues.push_back(value_t());
+ setProvided();
+ return mValues.back();
+ }
+
+ self_t& add(const value_t& item)
+ {
+ mValues.push_back(item);
+ setProvided();
+ return *this;
+ }
+
+ self_t& add(const typename named_value_t::name_t& name)
+ {
+ value_t value;
+
+ // try to parse a per type named value
+ if (named_value_t::getValueFromName(name, value))
+ {
+ add(value);
+ mValues.back().setValueName(name);
+ }
+ return *this;
+ }
+
+ // implicit conversion
+ operator const container_t&() const { return mValues; }
+ // explicit conversion
+ const container_t& operator()() const { return mValues; }
+
+ iterator begin() { return mValues.begin(); }
+ iterator end() { return mValues.end(); }
+ const_iterator begin() const { return mValues.begin(); }
+ const_iterator end() const { return mValues.end(); }
+ bool empty() const { return mValues.empty(); }
+ size_t size() const { return mValues.size(); }
+
+ size_t numValidElements() const
+ {
+ size_t count = 0;
+ for (const_iterator it = mValues.begin(), end_it = mValues.end();
+ it != end_it;
+ ++it)
+ {
+ if(it->isValid()) count++;
+ }
+ return count;
+ }
+
+ protected:
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (overwrite)
+ {
+ std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues));
+ }
+ else
+ {
+ container_t new_values(src_typed_param.mValues);
+ std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values));
+ std::swap(dst_typed_param.mValues, new_values);
+ }
+
+ if (src_typed_param.begin() != src_typed_param.end())
+ {
+ dst_typed_param.setProvided();
+ }
+
+ return true;
+ }
+
+ container_t mValues;
+ size_t mMinCount,
+ mMaxCount;
+
+ private:
+ void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name )
+ {
+ ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
+ block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count));
+ block_descriptor.addParam(param_descriptor, name);
+ }
+ };
+
+ template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
+ class ChoiceBlock : public BASE_BLOCK
+ {
+ typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> self_t;
+ typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> enclosing_block_t;
+ typedef BASE_BLOCK base_block_t;
+
+ LOG_CLASS(self_t);
+ public:
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const self_t& other)
+ {
+ return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true);
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const self_t& other)
+ {
+ return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false);
+ }
+
+ bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
+ {
+ bool source_override = source_provided && (overwrite || !dest_provided);
+
+ if (source_override || source.mCurChoice == mCurChoice)
+ {
+ return mergeBlock(block_data, source, overwrite);
+ }
+ return false;
+ }
+
+ // merge with other block
+ bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite)
+ {
+ mCurChoice = other.mCurChoice;
+ return base_block_t::mergeBlock(getBlockDescriptor(), other, overwrite);
+ }
+
+ // clear out old choice when param has changed
+ /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided)
+ {
+ param_handle_t changed_param_handle = base_block_t::getHandleFromParam(&changed_param);
+ // if we have a new choice...
+ if (changed_param_handle != mCurChoice)
+ {
+ // clear provided flag on previous choice
+ Param* previous_choice = base_block_t::getParamFromHandle(mCurChoice);
+ if (previous_choice)
+ {
+ previous_choice->setProvided(false);
+ }
+ mCurChoice = changed_param_handle;
+ }
+ base_block_t::paramChanged(changed_param, user_provided);
+ }
+
+ virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
+ virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }
+
+ protected:
+ ChoiceBlock()
+ : mCurChoice(0)
+ {
+ BaseBlock::init(getBlockDescriptor(), base_block_t::getBlockDescriptor(), sizeof(DERIVED_BLOCK));
+ }
+
+ // Alternatives are mutually exclusive wrt other Alternatives in the same block.
+ // One alternative in a block will always have isChosen() == true.
+ // At most one alternative in a block will have isProvided() == true.
+ template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
+ class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef typename super_t::value_t value_t;
+ typedef typename super_t::default_value_t default_value_t;
+
+ public:
+ friend class ChoiceBlock<DERIVED_BLOCK>;
+
+ using super_t::operator =;
+
+ explicit Alternative(const char* name = "", const default_value_t& val = defaultValue<default_value_t>())
+ : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1),
+ mOriginalValue(val)
+ {
+ // assign initial choice to first declared option
+ DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr);
+ if (LL_UNLIKELY(DERIVED_BLOCK::getBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING))
+ {
+ if(blockp->mCurChoice == 0)
+ {
+ blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this);
+ }
+ }
+ }
+
+ void choose()
+ {
+ static_cast<enclosing_block_t&>(Param::enclosingBlock()).paramChanged(*this, true);
+ }
+
+ void chooseAs(const value_t& val)
+ {
+ super_t::set(val);
+ }
+
+ void operator =(const value_t& val)
+ {
+ super_t::set(val);
+ }
+
+ void operator()(const value_t& val)
+ {
+ super_t::set(val);
+ }
+
+ operator const value_t&() const
+ {
+ return (*this)();
+ }
+
+ const value_t& operator()() const
+ {
+ if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+ {
+ return super_t::getValue();
+ }
+ return mOriginalValue;
+ }
+
+ bool isChosen() const
+ {
+ return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this;
+ }
+
+ private:
+ default_value_t mOriginalValue;
+ };
+
+ public:
+ static BlockDescriptor& getBlockDescriptor()
+ {
+ static BlockDescriptor sBlockDescriptor;
+ return sBlockDescriptor;
+ }
+
+ private:
+ param_handle_t mCurChoice;
+
+ const Param* getCurrentChoice() const
+ {
+ return base_block_t::getParamFromHandle(mCurChoice);
+ }
+ };
+
+ template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
+ class Block
+ : public BASE_BLOCK
+ {
+ typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t;
+
+ protected:
+ typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t;
+
+ public:
+ typedef BASE_BLOCK base_block_t;
+
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const self_t& other)
+ {
+ return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true);
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const self_t& other)
+ {
+ return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false);
+ }
+
+ virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); }
+ virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); }
+
+ protected:
+ Block()
+ {
+ //#pragma message("Parsing LLInitParam::Block")
+ BaseBlock::init(getBlockDescriptor(), BASE_BLOCK::getBlockDescriptor(), sizeof(DERIVED_BLOCK));
+ }
+
+ //
+ // Nested classes for declaring parameters
+ //
+ template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
+ class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef typename super_t::value_t value_t;
+ typedef typename super_t::default_value_t default_value_t;
+
+ public:
+ using super_t::operator();
+ using super_t::operator =;
+
+ explicit Optional(const char* name = "", const default_value_t& val = defaultValue<default_value_t>())
+ : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1)
+ {
+ //#pragma message("Parsing LLInitParam::Block::Optional")
+ }
+
+ Optional& operator =(const value_t& val)
+ {
+ super_t::set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(const value_t& val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+ };
+
+ template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
+ class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t;
+ typedef typename super_t::value_t value_t;
+ typedef typename super_t::default_value_t default_value_t;
+
+ public:
+ using super_t::operator();
+ using super_t::operator =;
+
+ // mandatory parameters require a name to be parseable
+ explicit Mandatory(const char* name = "", const default_value_t& val = defaultValue<default_value_t>())
+ : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, &validate, 1, 1)
+ {}
+
+ Mandatory& operator =(const value_t& val)
+ {
+ super_t::set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(const value_t& val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+
+ static bool validate(const Param* p)
+ {
+ // valid only if provided
+ return static_cast<const self_t*>(p)->isProvided();
+ }
+
+ };
+
+ template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t >
+ class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true>
+ {
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t;
+ typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t;
+ typedef typename super_t::container_t container_t;
+ typedef typename super_t::value_t value_t;
+
+ public:
+ typedef typename super_t::iterator iterator;
+ typedef typename super_t::const_iterator const_iterator;
+
+ explicit Multiple(const char* name = "")
+ : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount)
+ {}
+
+ Multiple& operator =(const container_t& val)
+ {
+ super_t::set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(const container_t& val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+
+ static bool validate(const Param* paramp)
+ {
+ size_t num_valid = ((super_t*)paramp)->numValidElements();
+ return RANGE::minCount <= num_valid && num_valid <= RANGE::maxCount;
+ }
+ };
+
+ // can appear in data files, but will ignored during parsing
+ // cannot read or write in code
+ class Ignored : public Param
+ {
+ public:
+ explicit Ignored(const char* name)
+ : Param(DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr)
+ {
+ BlockDescriptor& block_descriptor = DERIVED_BLOCK::getBlockDescriptor();
+ if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING))
+ {
+ ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor(
+ block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ NULL,
+ &deserializeParam,
+ NULL,
+ NULL,
+ NULL,
+ 0, S32_MAX));
+ block_descriptor.addParam(param_descriptor, name);
+ }
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ if (name_stack_range.first == name_stack_range.second)
+ {
+ //std::string message = llformat("Deprecated value %s ignored", getName().c_str());
+ //parser.parserWarning(message);
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ // can appear in data files, or be written to in code, but data will be ignored
+ // cannot be read in code
+ class Deprecated : public Ignored
+ {
+ public:
+ explicit Deprecated(const char* name) : Ignored(name) {}
+
+ // dummy writer interfaces
+ template<typename T>
+ Deprecated& operator =(const T& val)
+ {
+ // do nothing
+ return *this;
+ }
+
+ template<typename T>
+ DERIVED_BLOCK& operator()(const T& val)
+ {
+ // do nothing
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+
+ template<typename T>
+ void set(const T& val, bool flag_as_provided = true)
+ {
+ // do nothing
+ }
+ };
+
+ public:
+ static BlockDescriptor& getBlockDescriptor()
+ {
+ static BlockDescriptor sBlockDescriptor;
+ return sBlockDescriptor;
+ }
+
+ protected:
+ template <typename T, typename NAME_VALUE_LOOKUP, bool multiple, typename is_block>
+ void changeDefault(TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>& param,
+ const typename TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>::value_t& value)
+ {
+ if (!param.isProvided())
+ {
+ param.set(value, false);
+ }
+ }
+
+ };
+
+ template<typename T, typename BLOCK_T>
+ struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::IS_A_BLOCK>, BLOCK_T >, void>
+ {
+ typedef IS_A_BLOCK value_t;
+ };
+
+ template<typename T, typename BLOCK_T>
+ struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::NOT_A_BLOCK>, BLOCK_T >, void>
+ {
+ typedef NOT_BLOCK value_t;
+ };
+
+ template<typename T, typename BLOCK_IDENTIFIER>
+ struct IsBlock<ParamValue<BaseBlock::Atomic<T>, typename IsBlock<BaseBlock::Atomic<T> >::value_t >, BLOCK_IDENTIFIER>
+ {
+ typedef typename IsBlock<T>::value_t value_t;
+ };
+
+ template<typename T, typename BLOCK_IDENTIFIER>
+ struct IsBlock<ParamValue<BaseBlock::Sequential<T>, typename IsBlock<BaseBlock::Sequential<T> >::value_t >, BLOCK_IDENTIFIER>
+ {
+ typedef typename IsBlock<T>::value_t value_t;
+ };
+
+
+ template<typename T>
+ struct InnerMostType
+ {
+ typedef T value_t;
+ };
+
+ template<typename T>
+ struct InnerMostType<ParamValue<T, NOT_BLOCK> >
+ {
+ typedef typename InnerMostType<T>::value_t value_t;
+ };
+
+ template<typename T>
+ struct InnerMostType<ParamValue<T, IS_A_BLOCK> >
+ {
+ typedef typename InnerMostType<T>::value_t value_t;
+ };
+
+ template<typename T, typename BLOCK_T>
+ class ParamValue <BaseBlock::Atomic<T>, BLOCK_T>
+ {
+ typedef ParamValue <BaseBlock::Atomic<T>, BLOCK_T> self_t;
+
+ public:
+ typedef typename InnerMostType<T>::value_t value_t;
+ typedef T default_value_t;
+
+ ParamValue()
+ : mValue()
+ {}
+
+ ParamValue(const default_value_t& value)
+ : mValue(value)
+ {}
+
+ void setValue(const value_t& val)
+ {
+ mValue.setValue(val);
+ }
+
+ const value_t& getValue() const
+ {
+ return mValue.getValue();
+ }
+
+ value_t& getValue()
+ {
+ return mValue.getValue();
+ }
+
+ bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ if (new_name)
+ {
+ resetToDefault();
+ }
+ return mValue.deserializeBlock(p, name_stack_range, new_name);
+ }
+
+ bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
+ {
+ const BaseBlock* base_block = diff_block
+ ? &(diff_block->mValue)
+ : NULL;
+ return mValue.serializeBlock(p, name_stack, predicate_rule, base_block);
+ }
+
+ bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
+ {
+ return mValue.inspectBlock(p, name_stack, min_count, max_count);
+ }
+
+ bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
+ {
+ if ((overwrite && source_provided) // new values coming in on top or...
+ || (!overwrite && !dst_provided)) // values being pushed under with nothing already there
+ {
+ // clear away what is there and take the new stuff as a whole
+ resetToDefault();
+ return mValue.mergeBlock(block_data, source.getValue(), overwrite);
+ }
+ return mValue.mergeBlock(block_data, source.getValue(), overwrite);
+ }
+
+ bool validateBlock(bool emit_errors = true) const
+ {
+ return mValue.validateBlock(emit_errors);
+ }
+
+ bool isValid() const
+ {
+ return validateBlock(false);
+ }
+
+ static BlockDescriptor& getBlockDescriptor()
+ {
+ return value_t::getBlockDescriptor();
+ }
+
+
+ private:
+ void resetToDefault()
+ {
+ static T default_value;
+ mValue = default_value;
+ }
+
+ T mValue;
+ };
+
+ template<typename T>
+ class ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK>
+ {
+ typedef ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> self_t;
+
+ public:
+ typedef typename InnerMostType<T>::value_t value_t;
+ typedef T default_value_t;
+
+ ParamValue()
+ : mValue()
+ {
+ mCurParam = getBlockDescriptor().mAllParams.begin();
+ }
+
+ ParamValue(const default_value_t& value)
+ : mValue(value)
+ {
+ mCurParam = getBlockDescriptor().mAllParams.begin();
+ }
+
+ void setValue(const value_t& val)
+ {
+ mValue.setValue(val);
+ }
+
+ const value_t& getValue() const
+ {
+ return mValue.getValue();
+ }
+
+ value_t& getValue()
+ {
+ return mValue.getValue();
+ }
+
+ bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ if (new_name)
+ {
+ mCurParam = getBlockDescriptor().mAllParams.begin();
+ }
+ if (name_stack_range.first == name_stack_range.second
+ && mCurParam != getBlockDescriptor().mAllParams.end())
+ {
+ // deserialize to mCurParam
+ ParamDescriptor& pd = *(*mCurParam);
+ ParamDescriptor::deserialize_func_t deserialize_func = pd.mDeserializeFunc;
+ Param* paramp = mValue.getParamFromHandle(pd.mParamHandle);
+
+ if (deserialize_func
+ && paramp
+ && deserialize_func(*paramp, p, name_stack_range, new_name))
+ {
+ ++mCurParam;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return mValue.deserializeBlock(p, name_stack_range, new_name);
+ }
+ }
+
+ bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
+ {
+ const BaseBlock* base_block = diff_block
+ ? &(diff_block->mValue)
+ : NULL;
+ return mValue.serializeBlock(p, name_stack, predicate_rule, base_block);
+ }
+
+ bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
+ {
+ return mValue.inspectBlock(p, name_stack, min_count, max_count);
+ }
+
+ bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
+ {
+ return mValue.mergeBlock(block_data, source.getValue(), overwrite);
+ }
+
+ bool validateBlock(bool emit_errors = true) const
+ {
+ return mValue.validateBlock(emit_errors);
+ }
+
+ bool isValid() const
+ {
+ return validateBlock(false);
+ }
+
+ static BlockDescriptor& getBlockDescriptor()
+ {
+ return value_t::getBlockDescriptor();
+ }
+
+ private:
+
+ BlockDescriptor::all_params_list_t::iterator mCurParam;
+ T mValue;
+ };
+
+ template<typename T>
+ class ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK>
+ : public T
+ {
+ typedef ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> self_t;
+
+ public:
+ typedef typename InnerMostType<T>::value_t value_t;
+ typedef T default_value_t;
+
+ ParamValue()
+ : T()
+ {}
+
+ ParamValue(const default_value_t& value)
+ : T(value.getValue())
+ {}
+
+ bool isValid() const { return true; }
+ };
+
+ template<typename T, typename BLOCK_T>
+ class ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T>
+ {
+ typedef ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> self_t;
+
+ public:
+ typedef typename InnerMostType<T>::value_t value_t;
+ typedef LazyValue<T> default_value_t;
+
+ ParamValue()
+ : mValue()
+ {}
+
+ ParamValue(const default_value_t& other)
+ : mValue(other)
+ {}
+
+ ParamValue(const T& value)
+ : mValue(value)
+ {}
+
+ void setValue(const value_t& val)
+ {
+ mValue.set(val);
+ }
+
+ const value_t& getValue() const
+ {
+ return mValue.get().getValue();
+ }
+
+ value_t& getValue()
+ {
+ return mValue.get().getValue();
+ }
+
+ bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ return mValue.get().deserializeBlock(p, name_stack_range, new_name);
+ }
+
+ bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const
+ {
+ if (mValue.empty()) return false;
+
+ const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty())
+ ? &(diff_block->mValue.get().getValue())
+ : NULL;
+ return mValue.get().serializeBlock(p, name_stack, predicate_rule, base_block);
+ }
+
+ bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
+ {
+ return mValue.get().inspectBlock(p, name_stack, min_count, max_count);
+ }
+
+ bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite)
+ {
+ return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite);
+ }
+
+ bool validateBlock(bool emit_errors = true) const
+ {
+ return mValue.empty() || mValue.get().validateBlock(emit_errors);
+ }
+
+ bool isValid() const
+ {
+ return validateBlock(false);
+ }
+
+ static BlockDescriptor& getBlockDescriptor()
+ {
+ return value_t::getBlockDescriptor();
+ }
+
+ private:
+ LazyValue<T> mValue;
+ };
+
+ template<typename T, typename BLOCK_T>
+ class ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T>
+ {
+ typedef ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> self_t;
+
+ public:
+ typedef typename InnerMostType<T>::value_t value_t;
+ typedef LazyValue<T> default_value_t;
+
+ ParamValue()
+ : mValue()
+ {}
+
+ ParamValue(const default_value_t& other)
+ : mValue(other)
+ {}
+
+ ParamValue(const T& value)
+ : mValue(value)
+ {}
+
+ void setValue(const value_t& val)
+ {
+ mValue.set(val);
+ }
+
+ const value_t& getValue() const
+ {
+ return mValue.get().getValue();
+ }
+
+ value_t& getValue()
+ {
+ return mValue.get().getValue();
+ }
+
+ bool isValid() const
+ {
+ return true;
+ }
+
+ private:
+ LazyValue<T> mValue;
+ };
+
+ template <>
+ class ParamValue <LLSD, NOT_BLOCK>
+ : public BaseBlock
+ {
+ public:
+ typedef LLSD value_t;
+ typedef LLSD default_value_t;
+
+ ParamValue()
+ {}
+
+ ParamValue(const default_value_t& other)
+ : mValue(other)
+ {}
+
+ void setValue(const value_t& val) { mValue = val; }
+
+ const value_t& getValue() const { return mValue; }
+ LLSD& getValue() { return mValue; }
+
+ // block param interface
+ LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name);
+ LL_COMMON_API bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const;
+ bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const
+ {
+ //TODO: implement LLSD params as schema type Any
+ return true;
+ }
+
+ private:
+ static void serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack);
+
+ LLSD mValue;
+ };
+
+ template<typename T>
+ class CustomParamValue
+ : public Block<ParamValue<T> >
+ {
+ public:
+ typedef enum e_value_age
+ {
+ VALUE_NEEDS_UPDATE, // mValue needs to be refreshed from the block parameters
+ VALUE_AUTHORITATIVE, // mValue holds the authoritative value (which has been replicated to the block parameters via updateBlockFromValue)
+ BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative
+ } EValueAge;
+
+ typedef TypeValues<T> derived_t;
+ typedef CustomParamValue<T> self_t;
+ typedef Block<ParamValue<T> > block_t;
+ typedef T default_value_t;
+ typedef T value_t;
+ typedef void baseblock_base_class_t;
+
+
+ CustomParamValue(const default_value_t& value = T())
+ : mValue(value),
+ mValueAge(VALUE_AUTHORITATIVE)
+ {}
+
+ bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name)
+ {
+ derived_t& typed_param = static_cast<derived_t&>(*this);
+ // try to parse direct value T
+ if (name_stack_range.first == name_stack_range.second)
+ {
+ if(parser.readValue(typed_param.mValue))
+ {
+ typed_param.mValueAge = VALUE_AUTHORITATIVE;
+ typed_param.updateBlockFromValue(false);
+
+ return true;
+ }
+ }
+
+ // fall back on parsing block components for T
+ return typed_param.BaseBlock::deserializeBlock(parser, name_stack_range, new_name);
+ }
+
+ bool serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const
+ {
+ const derived_t& typed_param = static_cast<const derived_t&>(*this);
+ const derived_t* diff_param = static_cast<const derived_t*>(diff_block);
+
+ //std::string key = typed_param.getValueName();
+
+ //// first try to write out name of name/value pair
+ //if (!key.empty())
+ //{
+ // if (!diff_param || !ParamCompare<std::string>::equals(diff_param->getValueName(), key))
+ // {
+ // return parser.writeValue(key, name_stack);
+ // }
+ //}
+ // then try to serialize value directly
+ if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue()))
+ {
+
+ if (parser.writeValue(typed_param.getValue(), name_stack))
+ {
+ return true;
+ }
+ else
+ {
+ //RN: *always* serialize provided components of BlockValue (don't pass diff_param on),
+ // since these tend to be viewed as the constructor arguments for the value T. It seems
+ // cleaner to treat the uniqueness of a BlockValue according to the generated value, and
+ // not the individual components. This way <color red="0" green="1" blue="0"/> will not
+ // be exported as <color green="1"/>, since it was probably the intent of the user to
+ // be specific about the RGB color values. This also fixes an issue where we distinguish
+ // between rect.left not being provided and rect.left being explicitly set to 0 (same as default)
+
+ if (typed_param.mValueAge == VALUE_AUTHORITATIVE)
+ {
+ // if the value is authoritative but the parser doesn't accept the value type
+ // go ahead and make a copy, and splat the value out to its component params
+ // and serialize those params
+ derived_t copy(typed_param);
+ copy.updateBlockFromValue(true);
+ return copy.block_t::serializeBlock(parser, name_stack, predicate_rule, NULL);
+ }
+ else
+ {
+ return block_t::serializeBlock(parser, name_stack, predicate_rule, NULL);
+ }
+ }
+ }
+ return false;
+ }
+
+ bool validateBlock(bool emit_errors = true) const
+ {
+ if (mValueAge == VALUE_NEEDS_UPDATE)
+ {
+ if (block_t::validateBlock(emit_errors))
+ {
+ // clear stale keyword associated with old value
+ mValueAge = BLOCK_AUTHORITATIVE;
+ static_cast<derived_t*>(const_cast<self_t*>(this))->updateValueFromBlock();
+ return true;
+ }
+ else
+ {
+ //block value incomplete, so not considered provided
+ // will attempt to revalidate on next call to isProvided()
+ return false;
+ }
+ }
+ else
+ {
+ // we have a valid value in hand
+ return true;
+ }
+ }
+
+ // propagate change status up to enclosing block
+ /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided)
+ {
+ BaseBlock::paramChanged(changed_param, user_provided);
+ if (user_provided)
+ {
+ // a parameter changed, so our value is out of date
+ mValueAge = VALUE_NEEDS_UPDATE;
+ }
+ }
+
+ void setValue(const value_t& val)
+ {
+ // set param version number to be up to date, so we ignore block contents
+ mValueAge = VALUE_AUTHORITATIVE;
+ mValue = val;
+ static_cast<derived_t*>(this)->updateBlockFromValue(false);
+ }
+
+ const value_t& getValue() const
+ {
+ validateBlock(true);
+ return mValue;
+ }
+
+ T& getValue()
+ {
+ validateBlock(true);
+ return mValue;
+ }
+
+ protected:
+
+ // use this from within updateValueFromBlock() to set the value without making it authoritative
+ void updateValue(const value_t& value)
+ {
+ mValue = value;
+ }
+
+ bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
+ {
+ bool source_override = source_provided && (overwrite || !dst_provided);
+
+ const derived_t& src_typed_param = static_cast<const derived_t&>(source);
+
+ if (source_override && src_typed_param.mValueAge == VALUE_AUTHORITATIVE)
+ {
+ // copy value over
+ setValue(src_typed_param.getValue());
+ return true;
+ }
+ // merge individual parameters into destination
+ if (mValueAge == VALUE_AUTHORITATIVE)
+ {
+ static_cast<derived_t*>(this)->updateBlockFromValue(dst_provided);
+ }
+ return mergeBlock(block_data, source, overwrite);
+ }
+
+ bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& source, bool overwrite)
+ {
+ return block_t::mergeBlock(block_data, source, overwrite);
+ }
+
+ private:
+ mutable T mValue;
+ mutable EValueAge mValueAge;
+ };
+}
+
+
+#endif // LL_LLPARAM_H
diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp
index c962cb5be1..11fc53f8c8 100644..100755
--- a/indra/llcommon/llinstancetracker.cpp
+++ b/indra/llcommon/llinstancetracker.cpp
@@ -1,8 +1,25 @@
/**
* @file lllinstancetracker.cpp
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -10,11 +27,26 @@
#include "linden_common.h"
// associated header
#include "llinstancetracker.h"
+#include "llapr.h"
+
// STL headers
// std headers
// external library headers
// other Linden headers
-// llinstancetracker.h is presently header-only. This file exists only because our CMake
-// test macro ADD_BUILD_TEST requires it.
-int dummy = 0;
+void LLInstanceTrackerBase::StaticBase::incrementDepth()
+{
+ apr_atomic_inc32(&sIterationNestDepth);
+}
+
+void LLInstanceTrackerBase::StaticBase::decrementDepth()
+{
+ llassert(sIterationNestDepth);
+ apr_atomic_dec32(&sIterationNestDepth);
+}
+
+U32 LLInstanceTrackerBase::StaticBase::getDepth()
+{
+ apr_uint32_t data = apr_atomic_read32(&sIterationNestDepth);
+ return data;
+}
diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index 9df7998273..9783644e66 100644..100755
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -3,31 +3,25 @@
* @brief LLInstanceTracker is a mixin class that automatically tracks object
* instances with or without an associated key
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,162 +29,323 @@
#define LL_LLINSTANCETRACKER_H
#include <map>
+#include <typeinfo>
-#include "string_table.h"
-#include <boost/utility.hpp>
-#include <boost/function.hpp>
-#include <boost/bind.hpp>
+#include "llstringtable.h"
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
+/**
+ * Base class manages "class-static" data that must actually have singleton
+ * semantics: one instance per process, rather than one instance per module as
+ * sometimes happens with data simply declared static.
+ */
+class LL_COMMON_API LLInstanceTrackerBase
+{
+protected:
+ /// It's not essential to derive your STATICDATA (for use with
+ /// getStatic()) from StaticBase; it's just that both known
+ /// implementations do.
+ struct StaticBase
+ {
+ StaticBase():
+ sIterationNestDepth(0)
+ {}
+
+ void incrementDepth();
+ void decrementDepth();
+ U32 getDepth();
+ private:
+ U32 sIterationNestDepth;
+ };
+};
+
+LL_COMMON_API void assert_main_thread();
+
+enum EInstanceTrackerAllowKeyCollisions
+{
+ LLInstanceTrackerErrorOnCollision,
+ LLInstanceTrackerReplaceOnCollision
+};
+
/// This mix-in class adds support for tracking all instances of the specified class parameter T
/// The (optional) key associates a value of type KEY with a given instance of T, for quick lookup
/// If KEY is not provided, then instances are stored in a simple set
-/// @NOTE: see explicit specialization below for default KEY==T* case
-template<typename T, typename KEY = T*>
-class LLInstanceTracker : boost::noncopyable
+/// @NOTE: see explicit specialization below for default KEY==void case
+/// @NOTE: this class is not thread-safe unless used as read-only
+template<typename T, typename KEY = void, EInstanceTrackerAllowKeyCollisions KEY_COLLISION_BEHAVIOR = LLInstanceTrackerErrorOnCollision>
+class LLInstanceTracker : public LLInstanceTrackerBase
{
- typedef typename std::map<KEY, T*> InstanceMap;
- typedef boost::function<const KEY&(typename InstanceMap::value_type&)> KeyGetter;
- typedef boost::function<T*(typename InstanceMap::value_type&)> InstancePtrGetter;
+ typedef LLInstanceTracker<T, KEY> self_t;
+ typedef typename std::multimap<KEY, T*> InstanceMap;
+ struct StaticData: public StaticBase
+ {
+ InstanceMap sMap;
+ };
+ static StaticData& getStatic() { static StaticData sData; return sData;}
+ static InstanceMap& getMap_() { return getStatic().sMap; }
+
public:
- /// Dereferencing key_iter gives you a const KEY&
- typedef boost::transform_iterator<KeyGetter, typename InstanceMap::iterator> key_iter;
- /// Dereferencing instance_iter gives you a T&
- typedef boost::indirect_iterator< boost::transform_iterator<InstancePtrGetter, typename InstanceMap::iterator> > instance_iter;
+ class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>
+ {
+ public:
+ typedef boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag> super_t;
+
+ instance_iter(const typename InstanceMap::iterator& it)
+ : mIterator(it)
+ {
+ getStatic().incrementDepth();
+ }
+
+ ~instance_iter()
+ {
+ getStatic().decrementDepth();
+ }
+
+
+ private:
+ friend class boost::iterator_core_access;
+
+ void increment() { mIterator++; }
+ bool equal(instance_iter const& other) const
+ {
+ return mIterator == other.mIterator;
+ }
+
+ T& dereference() const
+ {
+ return *(mIterator->second);
+ }
+
+ typename InstanceMap::iterator mIterator;
+ };
+
+ class key_iter : public boost::iterator_facade<key_iter, KEY, boost::forward_traversal_tag>
+ {
+ public:
+ typedef boost::iterator_facade<key_iter, KEY, boost::forward_traversal_tag> super_t;
+
+ key_iter(typename InstanceMap::iterator it)
+ : mIterator(it)
+ {
+ getStatic().incrementDepth();
+ }
+
+ key_iter(const key_iter& other)
+ : mIterator(other.mIterator)
+ {
+ getStatic().incrementDepth();
+ }
+
+ ~key_iter()
+ {
+ getStatic().decrementDepth();
+ }
+
+
+ private:
+ friend class boost::iterator_core_access;
+
+ void increment() { mIterator++; }
+ bool equal(key_iter const& other) const
+ {
+ return mIterator == other.mIterator;
+ }
+
+ KEY& dereference() const
+ {
+ return const_cast<KEY&>(mIterator->first);
+ }
+
+ typename InstanceMap::iterator mIterator;
+ };
static T* getInstance(const KEY& k)
{
- typename InstanceMap::const_iterator found = getMap_().find(k);
- return (found == getMap_().end()) ? NULL : found->second;
+ const InstanceMap& map(getMap_());
+ typename InstanceMap::const_iterator found = map.find(k);
+ return (found == map.end()) ? NULL : found->second;
}
- static key_iter beginKeys()
- {
- return boost::make_transform_iterator(getMap_().begin(),
- boost::bind(&InstanceMap::value_type::first, _1));
+ static instance_iter beginInstances()
+ {
+ return instance_iter(getMap_().begin());
}
- static key_iter endKeys()
+
+ static instance_iter endInstances()
{
- return boost::make_transform_iterator(getMap_().end(),
- boost::bind(&InstanceMap::value_type::first, _1));
+ return instance_iter(getMap_().end());
+ }
+
+ static S32 instanceCount()
+ {
+ return getMap_().size();
}
- static instance_iter beginInstances()
+
+ static key_iter beginKeys()
{
- return instance_iter(boost::make_transform_iterator(getMap_().begin(),
- boost::bind(&InstanceMap::value_type::second, _1)));
+ return key_iter(getMap_().begin());
}
- static instance_iter endInstances()
+ static key_iter endKeys()
{
- return instance_iter(boost::make_transform_iterator(getMap_().end(),
- boost::bind(&InstanceMap::value_type::second, _1)));
+ return key_iter(getMap_().end());
}
- static S32 instanceCount() { return getMap_().size(); }
+
protected:
- LLInstanceTracker(KEY key) { add_(key); }
- virtual ~LLInstanceTracker() { remove_(); }
+ LLInstanceTracker(const KEY& key)
+ {
+ // make sure static data outlives all instances
+ getStatic();
+ add_(key);
+ }
+ virtual ~LLInstanceTracker()
+ {
+ // it's unsafe to delete instances of this type while all instances are being iterated over.
+ llassert_always(getStatic().getDepth() == 0);
+ remove_();
+ }
virtual void setKey(KEY key) { remove_(); add_(key); }
- virtual const KEY& getKey() const { return mKey; }
+ virtual const KEY& getKey() const { return mInstanceKey; }
private:
- void add_(KEY key)
+ LLInstanceTracker( const LLInstanceTracker& );
+ const LLInstanceTracker& operator=( const LLInstanceTracker& );
+
+ void add_(const KEY& key)
{
- mKey = key;
- getMap_()[key] = static_cast<T*>(this);
+ mInstanceKey = key;
+ InstanceMap& map = getMap_();
+ typename InstanceMap::iterator insertion_point_it = map.lower_bound(key);
+ if (insertion_point_it != map.end()
+ && insertion_point_it->first == key)
+ { // found existing entry with that key
+ switch(KEY_COLLISION_BEHAVIOR)
+ {
+ case LLInstanceTrackerErrorOnCollision:
+ {
+ // use assert here instead of LL_ERRS(), otherwise the error will be ignored since this call is made during global object initialization
+ llassert_always_msg(false, "Instance with this same key already exists!");
+ break;
+ }
+ case LLInstanceTrackerReplaceOnCollision:
+ {
+ // replace pointer, but leave key (should have compared equal anyway)
+ insertion_point_it->second = static_cast<T*>(this);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ else
+ { // new key
+ map.insert(insertion_point_it, std::make_pair(key, static_cast<T*>(this)));
+ }
}
void remove_()
{
- getMap_().erase(mKey);
+ InstanceMap& map = getMap_();
+ typename InstanceMap::iterator iter = map.find(mInstanceKey);
+ if (iter != map.end())
+ {
+ map.erase(iter);
+ }
}
- static InstanceMap& getMap_()
- {
- if (! sInstances)
- {
- sInstances = new InstanceMap;
- }
- return *sInstances;
- }
-
private:
-
- KEY mKey;
- static InstanceMap* sInstances;
+ KEY mInstanceKey;
};
-/// explicit specialization for default case where KEY is T*
+/// explicit specialization for default case where KEY is void
/// use a simple std::set<T*>
-template<typename T>
-class LLInstanceTracker<T, T*>
+template<typename T, EInstanceTrackerAllowKeyCollisions KEY_COLLISION_BEHAVIOR>
+class LLInstanceTracker<T, void, KEY_COLLISION_BEHAVIOR> : public LLInstanceTrackerBase
{
+ typedef LLInstanceTracker<T, void> self_t;
typedef typename std::set<T*> InstanceSet;
+ struct StaticData: public StaticBase
+ {
+ InstanceSet sSet;
+ };
+ static StaticData& getStatic() { static StaticData sData; return sData; }
+ static InstanceSet& getSet_() { return getStatic().sSet; }
+
public:
- /// Dereferencing key_iter gives you a T* (since T* is the key)
- typedef typename InstanceSet::iterator key_iter;
- /// Dereferencing instance_iter gives you a T&
- typedef boost::indirect_iterator<key_iter> instance_iter;
- /// for completeness of analogy with the generic implementation
- static T* getInstance(T* k) { return k; }
+ /**
+ * Does a particular instance still exist? Of course, if you already have
+ * a T* in hand, you need not call getInstance() to @em locate the
+ * instance -- unlike the case where getInstance() accepts some kind of
+ * key. Nonetheless this method is still useful to @em validate a
+ * particular T*, since each instance's destructor removes itself from the
+ * underlying set.
+ */
+ static T* getInstance(T* k)
+ {
+ const InstanceSet& set(getSet_());
+ typename InstanceSet::const_iterator found = set.find(k);
+ return (found == set.end())? NULL : *found;
+ }
static S32 instanceCount() { return getSet_().size(); }
- // Instantiate this to get access to iterators for this type. It's a 'guard' in the sense
- // that it treats deletes of this type as errors as long as there is an instance of
- // this class alive in scope somewhere (i.e. deleting while iterating is bad).
- class LLInstanceTrackerScopedGuard
+ class instance_iter : public boost::iterator_facade<instance_iter, T, boost::forward_traversal_tag>
{
public:
- LLInstanceTrackerScopedGuard()
+ instance_iter(const typename InstanceSet::iterator& it)
+ : mIterator(it)
{
- ++sIterationNestDepth;
+ getStatic().incrementDepth();
}
- ~LLInstanceTrackerScopedGuard()
+ instance_iter(const instance_iter& other)
+ : mIterator(other.mIterator)
{
- --sIterationNestDepth;
+ getStatic().incrementDepth();
}
- static instance_iter beginInstances() { return instance_iter(getSet_().begin()); }
- static instance_iter endInstances() { return instance_iter(getSet_().end()); }
- static key_iter beginKeys() { return getSet_().begin(); }
- static key_iter endKeys() { return getSet_().end(); }
+ ~instance_iter()
+ {
+ getStatic().decrementDepth();
+ }
+
+ private:
+ friend class boost::iterator_core_access;
+
+ void increment() { mIterator++; }
+ bool equal(instance_iter const& other) const
+ {
+ return mIterator == other.mIterator;
+ }
+
+ T& dereference() const
+ {
+ return **mIterator;
+ }
+
+ typename InstanceSet::iterator mIterator;
};
+ static instance_iter beginInstances() { return instance_iter(getSet_().begin()); }
+ static instance_iter endInstances() { return instance_iter(getSet_().end()); }
+
protected:
LLInstanceTracker()
{
- // it's safe but unpredictable to create instances of this type while all instances are being iterated over. I hate unpredictable. This assert will probably be turned on early in the next development cycle.
- //llassert(sIterationNestDepth == 0);
+ // make sure static data outlives all instances
+ getStatic();
getSet_().insert(static_cast<T*>(this));
}
virtual ~LLInstanceTracker()
{
// it's unsafe to delete instances of this type while all instances are being iterated over.
- llassert(sIterationNestDepth == 0);
+ llassert_always(getStatic().getDepth() == 0);
getSet_().erase(static_cast<T*>(this));
}
LLInstanceTracker(const LLInstanceTracker& other)
{
- //llassert(sIterationNestDepth == 0);
getSet_().insert(static_cast<T*>(this));
}
-
- static InstanceSet& getSet_()
- {
- if (! sInstances)
- {
- sInstances = new InstanceSet;
- }
- return *sInstances;
- }
-
- static InstanceSet* sInstances;
- static S32 sIterationNestDepth;
};
-template <typename T, typename KEY> typename LLInstanceTracker<T, KEY>::InstanceMap* LLInstanceTracker<T, KEY>::sInstances = NULL;
-template <typename T> typename LLInstanceTracker<T, T*>::InstanceSet* LLInstanceTracker<T, T*>::sInstances = NULL;
-template <typename T> S32 LLInstanceTracker<T, T*>::sIterationNestDepth = 0;
-
#endif
diff --git a/indra/llcommon/llkeythrottle.h b/indra/llcommon/llkeythrottle.h
index 7544ab1d11..1f576cc19e 100644..100755
--- a/indra/llcommon/llkeythrottle.h
+++ b/indra/llcommon/llkeythrottle.h
@@ -1,31 +1,25 @@
/**
* @file llkeythrottle.h
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llkeyusetracker.h b/indra/llcommon/llkeyusetracker.h
index 1dc29cec86..1fb222dd40 100644..100755
--- a/indra/llcommon/llkeyusetracker.h
+++ b/indra/llcommon/llkeyusetracker.h
@@ -2,31 +2,25 @@
* @file llkeyusetracker.h
* @brief Declaration of the LLKeyUseTracker class.
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lllazy.cpp b/indra/llcommon/lllazy.cpp
deleted file mode 100644
index 215095bc27..0000000000
--- a/indra/llcommon/lllazy.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * @file lllazy.cpp
- * @author Nat Goodspeed
- * @date 2009-01-28
- * @brief Implementation for lllazy.
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-// Precompiled header
-#include "linden_common.h"
-// associated header
-#include "lllazy.h"
-// STL headers
-// std headers
-// external library headers
-// other Linden headers
-
-// lllazy.h is presently header-only. This file exists only because our CMake
-// test macro ADD_BUILD_TEST requires it.
-int dummy = 0;
diff --git a/indra/llcommon/lllazy.h b/indra/llcommon/lllazy.h
deleted file mode 100644
index 2240954d98..0000000000
--- a/indra/llcommon/lllazy.h
+++ /dev/null
@@ -1,382 +0,0 @@
-/**
- * @file lllazy.h
- * @author Nat Goodspeed
- * @date 2009-01-22
- * @brief Lazy instantiation of specified type. Useful in conjunction with
- * Michael Feathers's "Extract and Override Getter" ("Working
- * Effectively with Legacy Code", p. 352).
- *
- * Quoting his synopsis of steps on p.355:
- *
- * 1. Identify the object you need a getter for.
- * 2. Extract all of the logic needed to create the object into a getter.
- * 3. Replace all uses of the object with calls to the getter, and initialize
- * the reference that holds the object to null in all constructors.
- * 4. Add the first-time logic to the getter so that the object is constructed
- * and assigned to the reference whenever the reference is null.
- * 5. Subclass the class and override the getter to provide an alternative
- * object for testing.
- *
- * It's the second half of bullet 3 (3b, as it were) that bothers me. I find
- * it all too easy to imagine adding pointer initializers to all but one
- * constructor... the one not exercised by my tests. That suggested using
- * (e.g.) boost::scoped_ptr<MyObject> so you don't have to worry about
- * destroying it either.
- *
- * However, introducing additional machinery allows us to encapsulate bullet 4
- * as well.
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-#if ! defined(LL_LLLAZY_H)
-#define LL_LLLAZY_H
-
-#include <boost/function.hpp>
-#include <boost/scoped_ptr.hpp>
-#include <boost/lambda/construct.hpp>
-#include <stdexcept>
-
-/// LLLazyCommon simply factors out of LLLazy<T> things that don't depend on
-/// its template parameter.
-class LLLazyCommon
-{
-public:
- /**
- * This exception is thrown if you try to replace an LLLazy<T>'s factory
- * (or T* instance) after it already has an instance in hand. Since T
- * might well be stateful, we can't know the effect of silently discarding
- * and replacing an existing instance, so we disallow it. This facility is
- * intended for testing, and in a test scenario we can definitely control
- * that.
- */
- struct InstanceChange: public std::runtime_error
- {
- InstanceChange(const std::string& what): std::runtime_error(what) {}
- };
-
-protected:
- /**
- * InstanceChange might be appropriate in a couple of different LLLazy<T>
- * methods. Factor out the common logic.
- */
- template <typename PTR>
- static void ensureNoInstance(const PTR& ptr)
- {
- if (ptr)
- {
- // Too late: we've already instantiated the lazy object. We don't
- // know whether it's stateful or not, so it's not safe to discard
- // the existing instance in favor of a replacement.
- throw InstanceChange("Too late to replace LLLazy instance");
- }
- }
-};
-
-/**
- * LLLazy<T> is useful when you have an outer class Outer that you're trying
- * to bring under unit test, that contains a data member difficult to
- * instantiate in a test harness. Typically the data member's class Inner has
- * many thorny dependencies. Feathers generally advocates "Extract and
- * Override Factory Method" (p. 350). But in C++, you can't call a derived
- * class override of a virtual method from the derived class constructor,
- * which limits applicability of "Extract and Override Factory Method." For
- * such cases Feathers presents "Extract and Override Getter" (p. 352).
- *
- * So we'll assume that your class Outer contains a member like this:
- * @code
- * Inner mInner;
- * @endcode
- *
- * LLLazy<Inner> can be used to replace this member. You can directly declare:
- * @code
- * LLLazy<Inner> mInner;
- * @endcode
- * and change references to mInner accordingly.
- *
- * (Alternatively, you can add a base class of the form
- * <tt>LLLazyBase<Inner></tt>. This is discussed further in the LLLazyBase<T>
- * documentation.)
- *
- * LLLazy<T> binds a <tt>boost::scoped_ptr<T></tt> and a factory functor
- * returning T*. You can either bind that functor explicitly or let it default
- * to the expression <tt>new T()</tt>.
- *
- * As long as LLLazy<T> remains unreferenced, its T remains uninstantiated.
- * The first time you use get(), <tt>operator*()</tt> or <tt>operator->()</tt>
- * it will instantiate its T and thereafter behave like a pointer to it.
- *
- * Thus, any existing reference to <tt>mInner.member</tt> should be replaced
- * with <tt>mInner->member</tt>. Any simple reference to @c mInner should be
- * replaced by <tt>*mInner</tt>.
- *
- * (If the original declaration was a pointer initialized in Outer's
- * constructor, e.g. <tt>Inner* mInner</tt>, so much the better. In that case
- * you should be able to drop in <tt>LLLazy<Inner></tt> without much change.)
- *
- * The support for "Extract and Override Getter" lies in the fact that you can
- * replace the factory functor -- or provide an explicit T*. Presumably this
- * is most useful from a test subclass -- which suggests that your @c mInner
- * member should be @c protected.
- *
- * Note that <tt>boost::lambda::new_ptr<T>()</tt> makes a dandy factory
- * functor, for either the set() method or LLLazy<T>'s constructor. If your T
- * requires constructor arguments, use an expression more like
- * <tt>boost::lambda::bind(boost::lambda::new_ptr<T>(), arg1, arg2, ...)</tt>.
- *
- * Of course the point of replacing the functor is to substitute a class that,
- * though referenced as Inner*, is not an Inner; presumably this is a testing
- * subclass of Inner (e.g. TestInner). Thus your test subclass TestOuter for
- * the containing class Outer will contain something like this:
- * @code
- * class TestOuter: public Outer
- * {
- * public:
- * TestOuter()
- * {
- * // mInner must be 'protected' rather than 'private'
- * mInner.set(boost::lambda::new_ptr<TestInner>());
- * }
- * ...
- * };
- * @endcode
- */
-template <typename T>
-class LLLazy: public LLLazyCommon
-{
-public:
- /// Any nullary functor returning T* will work as a Factory
- typedef boost::function<T* ()> Factory;
-
- /// The default LLLazy constructor uses <tt>new T()</tt> as its Factory
- LLLazy():
- mFactory(boost::lambda::new_ptr<T>())
- {}
-
- /// Bind an explicit Factory functor
- LLLazy(const Factory& factory):
- mFactory(factory)
- {}
-
- /// Reference T, instantiating it if this is the first access
- const T& get() const
- {
- if (! mInstance)
- {
- // use the bound Factory functor
- mInstance.reset(mFactory());
- }
- return *mInstance;
- }
-
- /// non-const get()
- T& get()
- {
- return const_cast<T&>(const_cast<const LLLazy<T>*>(this)->get());
- }
-
- /// operator*() is equivalent to get()
- const T& operator*() const { return get(); }
- /// operator*() is equivalent to get()
- T& operator*() { return get(); }
-
- /**
- * operator->() must return (something resembling) T*. It's tempting to
- * return the underlying boost::scoped_ptr<T>, but that would require
- * breaking out the lazy-instantiation logic from get() into a common
- * private method. Assume the pointer used for operator->() access is very
- * short-lived.
- */
- const T* operator->() const { return &get(); }
- /// non-const operator->()
- T* operator->() { return &get(); }
-
- /// set(Factory). This will throw InstanceChange if mInstance has already
- /// been set.
- void set(const Factory& factory)
- {
- ensureNoInstance(mInstance);
- mFactory = factory;
- }
-
- /// set(T*). This will throw InstanceChange if mInstance has already been
- /// set.
- void set(T* instance)
- {
- ensureNoInstance(mInstance);
- mInstance.reset(instance);
- }
-
-private:
- Factory mFactory;
- // Consider an LLLazy<T> member of a class we're accessing by const
- // reference. We want to allow even const methods to touch the LLLazy<T>
- // member. Hence the actual pointer must be mutable because such access
- // might assign it.
- mutable boost::scoped_ptr<T> mInstance;
-};
-
-#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
-// Not gcc at all, or a gcc more recent than gcc 3.3
-#define GCC33 0
-#else
-#define GCC33 1
-#endif
-
-/**
- * LLLazyBase<T> wraps LLLazy<T>, giving you an alternative way to replace
- * <tt>Inner mInner;</tt>. Instead of coding <tt>LLLazy<Inner> mInner</tt>,
- * you can add LLLazyBase<Inner> to your Outer class's bases, e.g.:
- * @code
- * class Outer: public LLLazyBase<Inner>
- * {
- * ...
- * };
- * @endcode
- *
- * This gives you @c public get() and @c protected set() methods without
- * having to make your LLLazy<Inner> member @c protected. The tradeoff is that
- * you must access the wrapped LLLazy<Inner> using get() and set() rather than
- * with <tt>operator*()</tt> or <tt>operator->()</tt>.
- *
- * This mechanism can be used for more than one member, but only if they're of
- * different types. That is, you can replace:
- * @code
- * DifficultClass mDifficult;
- * AwkwardType mAwkward;
- * @endcode
- * with:
- * @code
- * class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
- * {
- * ...
- * };
- * @endcode
- * but for a situation like this:
- * @code
- * DifficultClass mMainDifficult, mAuxDifficult;
- * @endcode
- * you should directly embed LLLazy<DifficultClass> (q.v.).
- *
- * For multiple LLLazyBase bases, e.g. the <tt>LLLazyBase<DifficultClass>,
- * LLLazyBase<AwkwardType></tt> example above, access the relevant get()/set()
- * as (e.g.) <tt>LLLazyBase<DifficultClass>::get()</tt>. (This is why you
- * can't have multiple LLLazyBase<T> of the same T.) For a bit of syntactic
- * sugar, please see getLazy()/setLazy().
- */
-template <typename T>
-class LLLazyBase
-{
-public:
- /// invoke default LLLazy constructor
- LLLazyBase() {}
- /// make wrapped LLLazy bind an explicit Factory
- LLLazyBase(const typename LLLazy<T>::Factory& factory):
- mInstance(factory)
- {}
-
- /// access to LLLazy::get()
- T& get() { return *mInstance; }
- /// access to LLLazy::get()
- const T& get() const { return *mInstance; }
-
-protected:
- // see getLazy()/setLazy()
- #if (! GCC33)
- template <typename T2, class MYCLASS> friend T2& getLazy(MYCLASS* this_);
- template <typename T2, class MYCLASS> friend const T2& getLazy(const MYCLASS* this_);
- #else // gcc 3.3
- template <typename T2, class MYCLASS> friend T2& getLazy(const MYCLASS* this_);
- #endif // gcc 3.3
- template <typename T2, class MYCLASS> friend void setLazy(MYCLASS* this_, T2* instance);
- template <typename T2, class MYCLASS>
- friend void setLazy(MYCLASS* this_, const typename LLLazy<T2>::Factory& factory);
-
- /// access to LLLazy::set(Factory)
- void set(const typename LLLazy<T>::Factory& factory)
- {
- mInstance.set(factory);
- }
-
- /// access to LLLazy::set(T*)
- void set(T* instance)
- {
- mInstance.set(instance);
- }
-
-private:
- LLLazy<T> mInstance;
-};
-
-/**
- * @name getLazy()/setLazy()
- * Suppose you have something like the following:
- * @code
- * class Outer: public LLLazyBase<DifficultClass>, public LLLazyBase<AwkwardType>
- * {
- * ...
- * };
- * @endcode
- *
- * Your methods can reference the @c DifficultClass instance using
- * <tt>LLLazyBase<DifficultClass>::get()</tt>, which is admittedly a bit ugly.
- * Alternatively, you can write <tt>getLazy<DifficultClass>(this)</tt>, which
- * is somewhat more straightforward to read.
- *
- * Similarly,
- * @code
- * LLLazyBase<DifficultClass>::set(new TestDifficultClass());
- * @endcode
- * could instead be written:
- * @code
- * setLazy<DifficultClass>(this, new TestDifficultClass());
- * @endcode
- *
- * @note
- * I wanted to provide getLazy() and setLazy() without explicitly passing @c
- * this. That would imply making them methods on a base class rather than free
- * functions. But if <tt>LLLazyBase<T></tt> derives normally from (say) @c
- * LLLazyGrandBase providing those methods, then unqualified getLazy() would
- * be ambiguous: you'd have to write <tt>LLLazyBase<T>::getLazy<T>()</tt>,
- * which is even uglier than <tt>LLLazyBase<T>::get()</tt>, and therefore
- * pointless. You can make the compiler not care which @c LLLazyGrandBase
- * instance you're talking about by making @c LLLazyGrandBase a @c virtual
- * base class of @c LLLazyBase. But in that case,
- * <tt>LLLazyGrandBase::getLazy<T>()</tt> can't access
- * <tt>LLLazyBase<T>::get()</tt>!
- *
- * We want <tt>getLazy<T>()</tt> to access <tt>LLLazyBase<T>::get()</tt> as if
- * in the lexical context of some subclass method. Ironically, free functions
- * let us do that better than methods on a @c virtual base class -- but that
- * implies passing @c this explicitly. So be it.
- */
-//@{
-#if (! GCC33)
-template <typename T, class MYCLASS>
-T& getLazy(MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
-template <typename T, class MYCLASS>
-const T& getLazy(const MYCLASS* this_) { return this_->LLLazyBase<T>::get(); }
-#else // gcc 3.3
-// For const-correctness, we really should have two getLazy() variants: one
-// accepting const MYCLASS* and returning const T&, the other accepting
-// non-const MYCLASS* and returning non-const T&. This works fine on the Mac
-// (gcc 4.0.1) and Windows (MSVC 8.0), but fails on our Linux 32-bit Debian
-// Sarge stations (gcc 3.3.5). Since I really don't know how to beat that aging
-// compiler over the head to make it do the right thing, I'm going to have to
-// move forward with the wrong thing: a single getLazy() function that accepts
-// const MYCLASS* and returns non-const T&.
-template <typename T, class MYCLASS>
-T& getLazy(const MYCLASS* this_) { return const_cast<MYCLASS*>(this_)->LLLazyBase<T>::get(); }
-#endif // gcc 3.3
-template <typename T, class MYCLASS>
-void setLazy(MYCLASS* this_, T* instance) { this_->LLLazyBase<T>::set(instance); }
-template <typename T, class MYCLASS>
-void setLazy(MYCLASS* this_, const typename LLLazy<T>::Factory& factory)
-{
- this_->LLLazyBase<T>::set(factory);
-}
-//@}
-
-#endif /* ! defined(LL_LLLAZY_H) */
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
new file mode 100755
index 0000000000..84d2a12f65
--- /dev/null
+++ b/indra/llcommon/llleap.cpp
@@ -0,0 +1,459 @@
+/**
+ * @file llleap.cpp
+ * @author Nat Goodspeed
+ * @date 2012-02-20
+ * @brief Implementation for llleap.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleap.h"
+// STL headers
+#include <sstream>
+#include <algorithm>
+// std headers
+// external library headers
+#include <boost/bind.hpp>
+#include <boost/scoped_ptr.hpp>
+#include <boost/tokenizer.hpp>
+// other Linden headers
+#include "llerror.h"
+#include "llstring.h"
+#include "llprocess.h"
+#include "llevents.h"
+#include "stringize.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llerrorcontrol.h"
+#include "lltimer.h"
+#include "lluuid.h"
+#include "llleaplistener.h"
+
+#if LL_MSVC
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+LLLeap::LLLeap() {}
+LLLeap::~LLLeap() {}
+
+class LLLeapImpl: public LLLeap
+{
+ LOG_CLASS(LLLeap);
+public:
+ // Called only by LLLeap::create()
+ LLLeapImpl(const std::string& desc, const std::vector<std::string>& plugin):
+ // We might reassign mDesc in the constructor body if it's empty here.
+ mDesc(desc),
+ // We expect multiple LLLeapImpl instances. Definitely tweak
+ // mDonePump's name for uniqueness.
+ mDonePump("LLLeap", true),
+ // Troubling thought: what if one plugin intentionally messes with
+ // another plugin? LLEventPump names are in a single global namespace.
+ // Try to make that more difficult by generating a UUID for the reply-
+ // pump name -- so it should NOT need tweaking for uniqueness.
+ mReplyPump(LLUUID::generateNewID().asString()),
+ mExpect(0),
+ mPrevFatalFunction(LLError::getFatalFunction()),
+ // Instantiate a distinct LLLeapListener for this plugin. (Every
+ // plugin will want its own collection of managed listeners, etc.)
+ // Pass it a callback to our connect() method, so it can send events
+ // from a particular LLEventPump to the plugin without having to know
+ // this class or method name.
+ mListener(new LLLeapListener(boost::bind(&LLLeapImpl::connect, this, _1, _2)))
+ {
+ // Rule out empty vector
+ if (plugin.empty())
+ {
+ throw Error("no plugin command");
+ }
+
+ // Don't leave desc empty either, but in this case, if we weren't
+ // given one, we'll fake one.
+ if (desc.empty())
+ {
+ mDesc = LLProcess::basename(plugin[0]);
+ // how about a toLower() variant that returns the transformed string?!
+ std::string desclower(mDesc);
+ LLStringUtil::toLower(desclower);
+ // If we're running a Python script, use the script name for the
+ // desc instead of just 'python'. Arguably we should check for
+ // more different interpreters as well, but there's a reason to
+ // notice Python specially: we provide Python LLSD serialization
+ // support, so there's a pretty good reason to implement plugins
+ // in that language.
+ if (plugin.size() >= 2 && (desclower == "python" || desclower == "python.exe"))
+ {
+ mDesc = LLProcess::basename(plugin[1]);
+ }
+ }
+
+ // Listen for child "termination" right away to catch launch errors.
+ mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::bad_launch, this, _1));
+
+ // Okay, launch child.
+ LLProcess::Params params;
+ params.desc = mDesc;
+ std::vector<std::string>::const_iterator pi(plugin.begin()), pend(plugin.end());
+ params.executable = *pi++;
+ for ( ; pi != pend; ++pi)
+ {
+ params.args.add(*pi);
+ }
+ params.files.add(LLProcess::FileParam("pipe")); // stdin
+ params.files.add(LLProcess::FileParam("pipe")); // stdout
+ params.files.add(LLProcess::FileParam("pipe")); // stderr
+ params.postend = mDonePump.getName();
+ mChild = LLProcess::create(params);
+ // If that didn't work, no point in keeping this LLLeap object.
+ if (! mChild)
+ {
+ throw Error(STRINGIZE("failed to run " << mDesc));
+ }
+
+ // Okay, launch apparently worked. Change our mDonePump listener.
+ mDonePump.stopListening("LLLeap");
+ mDonePump.listen("LLLeap", boost::bind(&LLLeapImpl::done, this, _1));
+
+ // Child might pump large volumes of data through either stdout or
+ // stderr. Don't bother copying all that data into notification event.
+ LLProcess::ReadPipe
+ &childout(mChild->getReadPipe(LLProcess::STDOUT)),
+ &childerr(mChild->getReadPipe(LLProcess::STDERR));
+ childout.setLimit(20);
+ childerr.setLimit(20);
+
+ // Serialize any event received on mReplyPump to our child's stdin.
+ mStdinConnection = connect(mReplyPump, "LLLeap");
+
+ // Listening on stdout is stateful. In general, we're either waiting
+ // for the length prefix or waiting for the specified length of data.
+ // We address that with two different listener methods -- one of which
+ // is blocked at any given time.
+ mStdoutConnection = childout.getPump()
+ .listen("prefix", boost::bind(&LLLeapImpl::rstdout, this, _1));
+ mStdoutDataConnection = childout.getPump()
+ .listen("data", boost::bind(&LLLeapImpl::rstdoutData, this, _1));
+ mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
+
+ // Log anything sent up through stderr. When a typical program
+ // encounters an error, it writes its error message to stderr and
+ // terminates with nonzero exit code. In particular, the Python
+ // interpreter behaves that way. More generally, though, a plugin
+ // author can log whatever s/he wants to the viewer log using stderr.
+ mStderrConnection = childerr.getPump()
+ .listen("LLLeap", boost::bind(&LLLeapImpl::rstderr, this, _1));
+
+ // For our lifespan, intercept any LL_ERRS so we can notify plugin
+ LLError::setFatalFunction(boost::bind(&LLLeapImpl::fatalFunction, this, _1));
+
+ // Send child a preliminary event reporting our own reply-pump name --
+ // which would otherwise be pretty tricky to guess!
+ wstdin(mReplyPump.getName(),
+ LLSDMap
+ ("command", mListener->getName())
+ // Include LLLeap features -- this may be important for child to
+ // construct (or recognize) current protocol.
+ ("features", LLLeapListener::getFeatures()));
+ }
+
+ // Normally we'd expect to arrive here only via done()
+ virtual ~LLLeapImpl()
+ {
+ LL_DEBUGS("LLLeap") << "destroying LLLeap(\"" << mDesc << "\")" << LL_ENDL;
+ // Restore original FatalFunction
+ LLError::setFatalFunction(mPrevFatalFunction);
+ }
+
+ // Listener for failed launch attempt
+ bool bad_launch(const LLSD& data)
+ {
+ LL_WARNS("LLLeap") << data["string"].asString() << LL_ENDL;
+ return false;
+ }
+
+ // Listener for child-process termination
+ bool done(const LLSD& data)
+ {
+ // Log the termination
+ LL_INFOS("LLLeap") << data["string"].asString() << LL_ENDL;
+
+ // Any leftover data at this moment are because protocol was not
+ // satisfied. Possibly the child was interrupted in the middle of
+ // sending a message, possibly the child didn't flush stdout before
+ // terminating, possibly it's just garbage. Log its existence but
+ // discard it.
+ LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
+ if (childout.size())
+ {
+ LLProcess::ReadPipe::size_type
+ peeklen((std::min)(LLProcess::ReadPipe::size_type(50), childout.size()));
+ LL_WARNS("LLLeap") << "Discarding final " << childout.size() << " bytes: "
+ << childout.peek(0, peeklen) << "..." << LL_ENDL;
+ }
+
+ // Kill this instance. MUST BE LAST before return!
+ delete this;
+ return false;
+ }
+
+ // Listener for events on mReplyPump: send to child stdin
+ bool wstdin(const std::string& pump, const LLSD& data)
+ {
+ LLSD packet(LLSDMap("pump", pump)("data", data));
+
+ std::ostringstream buffer;
+ buffer << LLSDNotationStreamer(packet);
+
+/*==========================================================================*|
+ // DEBUGGING ONLY: don't copy str() if we can avoid it.
+ std::string strdata(buffer.str());
+ if (std::size_t(buffer.tellp()) != strdata.length())
+ {
+ LL_ERRS("LLLeap") << "tellp() -> " << buffer.tellp() << " != "
+ << "str().length() -> " << strdata.length() << LL_ENDL;
+ }
+ // DEBUGGING ONLY: reading back is terribly inefficient.
+ std::istringstream readback(strdata);
+ LLSD echo;
+ LLPointer<LLSDParser> parser(new LLSDNotationParser());
+ S32 parse_status(parser->parse(readback, echo, strdata.length()));
+ if (parse_status == LLSDParser::PARSE_FAILURE)
+ {
+ LL_ERRS("LLLeap") << "LLSDNotationParser() cannot parse output of "
+ << "LLSDNotationStreamer()" << LL_ENDL;
+ }
+ if (! llsd_equals(echo, packet))
+ {
+ LL_ERRS("LLLeap") << "LLSDNotationParser() produced different LLSD "
+ << "than passed to LLSDNotationStreamer()" << LL_ENDL;
+ }
+|*==========================================================================*/
+
+ LL_DEBUGS("EventHost") << "Sending: " << buffer.tellp() << ':';
+ std::string::size_type truncate(80);
+ if (buffer.tellp() <= truncate)
+ {
+ LL_CONT << buffer.str();
+ }
+ else
+ {
+ LL_CONT << buffer.str().substr(0, truncate) << "...";
+ }
+ LL_CONT << LL_ENDL;
+
+ LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
+ childin.get_ostream() << buffer.tellp() << ':' << buffer.str() << std::flush;
+ return false;
+ }
+
+ // Initial state of stateful listening on child stdout: wait for a length
+ // prefix, followed by ':'.
+ bool rstdout(const LLSD& data)
+ {
+ LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
+ // It's possible we got notified of a couple digit characters without
+ // seeing the ':' -- unlikely, but still. Until we see ':', keep
+ // waiting.
+ if (childout.contains(':'))
+ {
+ std::istream& childstream(childout.get_istream());
+ // Saw ':', read length prefix and store in mExpect.
+ size_t expect;
+ childstream >> expect;
+ int colon(childstream.get());
+ if (colon != ':')
+ {
+ // Protocol failure. Clear out the rest of the pending data in
+ // childout (well, up to a max length) to log what was wrong.
+ LLProcess::ReadPipe::size_type
+ readlen((std::min)(childout.size(), LLProcess::ReadPipe::size_type(80)));
+ bad_protocol(STRINGIZE(expect << char(colon) << childout.read(readlen)));
+ }
+ else
+ {
+ // Saw length prefix, saw colon, life is good. Now wait for
+ // that length of data to arrive.
+ mExpect = expect;
+ LL_DEBUGS("LLLeap") << "got length, waiting for "
+ << mExpect << " bytes of data" << LL_ENDL;
+ // Block calls to this method; resetting mBlocker unblocks
+ // calls to the other method.
+ mBlocker.reset(new LLEventPump::Blocker(mStdoutConnection));
+ // Go check if we've already received all the advertised data.
+ if (childout.size())
+ {
+ LLSD updata(data);
+ updata["len"] = LLSD::Integer(childout.size());
+ rstdoutData(updata);
+ }
+ }
+ }
+ else if (childout.contains('\n'))
+ {
+ // Since this is the initial listening state, this is where we'd
+ // arrive if the child isn't following protocol at all -- say
+ // because the user specified 'ls' or some darn thing.
+ bad_protocol(childout.getline());
+ }
+ return false;
+ }
+
+ // State in which we listen on stdout for the specified length of data to
+ // arrive.
+ bool rstdoutData(const LLSD& data)
+ {
+ LLProcess::ReadPipe& childout(mChild->getReadPipe(LLProcess::STDOUT));
+ // Until we've accumulated the promised length of data, keep waiting.
+ if (childout.size() >= mExpect)
+ {
+ // Ready to rock and roll.
+ LL_DEBUGS("LLLeap") << "needed " << mExpect << " bytes, got "
+ << childout.size() << ", parsing LLSD" << LL_ENDL;
+ LLSD data;
+ LLPointer<LLSDParser> parser(new LLSDNotationParser());
+ S32 parse_status(parser->parse(childout.get_istream(), data, mExpect));
+ if (parse_status == LLSDParser::PARSE_FAILURE)
+ {
+ bad_protocol("unparseable LLSD data");
+ }
+ else if (! (data.isMap() && data["pump"].isString() && data.has("data")))
+ {
+ // we got an LLSD object, but it lacks required keys
+ bad_protocol("missing 'pump' or 'data'");
+ }
+ else
+ {
+ // The LLSD object we got from our stream contains the keys we
+ // need.
+ LLEventPumps::instance().obtain(data["pump"]).post(data["data"]);
+ // Block calls to this method; resetting mBlocker unblocks calls
+ // to the other method.
+ mBlocker.reset(new LLEventPump::Blocker(mStdoutDataConnection));
+ // Go check for any more pending events in the buffer.
+ if (childout.size())
+ {
+ LLSD updata(data);
+ data["len"] = LLSD::Integer(childout.size());
+ rstdout(updata);
+ }
+ }
+ }
+ return false;
+ }
+
+ void bad_protocol(const std::string& data)
+ {
+ LL_WARNS("LLLeap") << mDesc << ": invalid protocol: " << data << LL_ENDL;
+ // No point in continuing to run this child.
+ mChild->kill();
+ }
+
+ // Listen on child stderr and log everything that arrives
+ bool rstderr(const LLSD& data)
+ {
+ LLProcess::ReadPipe& childerr(mChild->getReadPipe(LLProcess::STDERR));
+ // We might have gotten a notification involving only a partial line
+ // -- or multiple lines. Read all complete lines; stop when there's
+ // only a partial line left.
+ while (childerr.contains('\n'))
+ {
+ // DO NOT make calls with side effects in a logging statement! If
+ // that log level is suppressed, your side effects WON'T HAPPEN.
+ std::string line(childerr.getline());
+ // Log the received line. Prefix it with the desc so we know which
+ // plugin it's from. This method name rstderr() is intentionally
+ // chosen to further qualify the log output.
+ LL_INFOS("LLLeap") << mDesc << ": " << line << LL_ENDL;
+ }
+ // What if child writes a final partial line to stderr?
+ if (data["eof"].asBoolean() && childerr.size())
+ {
+ std::string rest(childerr.read(childerr.size()));
+ // Read all remaining bytes and log.
+ LL_INFOS("LLLeap") << mDesc << ": " << rest << LL_ENDL;
+ }
+ return false;
+ }
+
+ void fatalFunction(const std::string& error)
+ {
+ // Notify plugin
+ LLSD event;
+ event["type"] = "error";
+ event["error"] = error;
+ mReplyPump.post(event);
+
+ // All the above really accomplished was to buffer the serialized
+ // event in our WritePipe. Have to pump mainloop a couple times to
+ // really write it out there... but time out in case we can't write.
+ LLProcess::WritePipe& childin(mChild->getWritePipe(LLProcess::STDIN));
+ LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
+ LLSD nop;
+ F64 until = (LLTimer::getElapsedSeconds() + 2).value();
+ while (childin.size() && LLTimer::getElapsedSeconds() < until)
+ {
+ mainloop.post(nop);
+ }
+
+ // forward the call to the previous FatalFunction
+ mPrevFatalFunction(error);
+ }
+
+private:
+ /// We always want to listen on mReplyPump with wstdin(); under some
+ /// circumstances we'll also echo other LLEventPumps to the plugin.
+ LLBoundListener connect(LLEventPump& pump, const std::string& listener)
+ {
+ // Serialize any event received on the specified LLEventPump to our
+ // child's stdin, suitably enriched with the pump name on which it was
+ // received.
+ return pump.listen(listener,
+ boost::bind(&LLLeapImpl::wstdin, this, pump.getName(), _1));
+ }
+
+ std::string mDesc;
+ LLEventStream mDonePump;
+ LLEventStream mReplyPump;
+ LLProcessPtr mChild;
+ LLTempBoundListener
+ mStdinConnection, mStdoutConnection, mStdoutDataConnection, mStderrConnection;
+ boost::scoped_ptr<LLEventPump::Blocker> mBlocker;
+ LLProcess::ReadPipe::size_type mExpect;
+ LLError::FatalFunction mPrevFatalFunction;
+ boost::scoped_ptr<LLLeapListener> mListener;
+};
+
+// This must follow the declaration of LLLeapImpl, so it may as well be last.
+LLLeap* LLLeap::create(const std::string& desc, const std::vector<std::string>& plugin, bool exc)
+{
+ // If caller is willing to permit exceptions, just instantiate.
+ if (exc)
+ return new LLLeapImpl(desc, plugin);
+
+ // Caller insists on suppressing LLLeap::Error. Very well, catch it.
+ try
+ {
+ return new LLLeapImpl(desc, plugin);
+ }
+ catch (const LLLeap::Error&)
+ {
+ return NULL;
+ }
+}
+
+LLLeap* LLLeap::create(const std::string& desc, const std::string& plugin, bool exc)
+{
+ // Use LLStringUtil::getTokens() to parse the command line
+ return create(desc,
+ LLStringUtil::getTokens(plugin,
+ " \t\r\n", // drop_delims
+ "", // no keep_delims
+ "\"'", // either kind of quotes
+ "\\"), // backslash escape
+ exc);
+}
diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h
new file mode 100755
index 0000000000..e33f25e530
--- /dev/null
+++ b/indra/llcommon/llleap.h
@@ -0,0 +1,81 @@
+/**
+ * @file llleap.h
+ * @author Nat Goodspeed
+ * @date 2012-02-20
+ * @brief Class that implements "LLSD Event API Plugin"
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLLEAP_H)
+#define LL_LLLEAP_H
+
+#include "llinstancetracker.h"
+#include <string>
+#include <vector>
+#include <stdexcept>
+
+/**
+ * LLSD Event API Plugin class. Because instances are managed by
+ * LLInstanceTracker, you can instantiate LLLeap and forget the instance
+ * unless you need it later. Each instance manages an LLProcess; when the
+ * child process terminates, LLLeap deletes itself. We don't require a unique
+ * LLInstanceTracker key.
+ *
+ * The fact that a given LLLeap instance vanishes when its child process
+ * terminates makes it problematic to store an LLLeap* anywhere. Any stored
+ * LLLeap* pointer should be validated before use by
+ * LLLeap::getInstance(LLLeap*) (see LLInstanceTracker).
+ */
+class LL_COMMON_API LLLeap: public LLInstanceTracker<LLLeap>
+{
+public:
+
+ /**
+ * Pass a brief string description, mostly for logging purposes. The desc
+ * need not be unique, but obviously the clearer we can make it, the
+ * easier these things will be to debug. The strings are the command line
+ * used to launch the desired plugin process.
+ *
+ * Pass exc=false to suppress LLLeap::Error exception. Obviously in that
+ * case the caller cannot discover the nature of the error, merely that an
+ * error of some kind occurred (because create() returned NULL). Either
+ * way, the error is logged.
+ */
+ static LLLeap* create(const std::string& desc, const std::vector<std::string>& plugin,
+ bool exc=true);
+
+ /**
+ * Pass a brief string description, mostly for logging purposes. The desc
+ * need not be unique, but obviously the clearer we can make it, the
+ * easier these things will be to debug. Pass a command-line string
+ * to launch the desired plugin process.
+ *
+ * Pass exc=false to suppress LLLeap::Error exception. Obviously in that
+ * case the caller cannot discover the nature of the error, merely that an
+ * error of some kind occurred (because create() returned NULL). Either
+ * way, the error is logged.
+ */
+ static LLLeap* create(const std::string& desc, const std::string& plugin,
+ bool exc=true);
+
+ /**
+ * Exception thrown for invalid create() arguments, e.g. no plugin
+ * program. This is more resiliant than an LL_ERRS failure, because the
+ * string(s) passed to create() might come from an external source. This
+ * way the caller can catch LLLeap::Error and try to recover.
+ */
+ struct Error: public std::runtime_error
+ {
+ Error(const std::string& what): std::runtime_error(what) {}
+ };
+
+ virtual ~LLLeap();
+
+protected:
+ LLLeap();
+};
+
+#endif /* ! defined(LL_LLLEAP_H) */
diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp
new file mode 100755
index 0000000000..fa5730f112
--- /dev/null
+++ b/indra/llcommon/llleaplistener.cpp
@@ -0,0 +1,287 @@
+/**
+ * @file llleaplistener.cpp
+ * @author Nat Goodspeed
+ * @date 2012-03-16
+ * @brief Implementation for llleaplistener.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleaplistener.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "lluuid.h"
+#include "llsdutil.h"
+#include "stringize.h"
+
+/*****************************************************************************
+* LEAP FEATURE STRINGS
+*****************************************************************************/
+/**
+ * Implement "getFeatures" command. The LLSD map thus obtained is intended to
+ * be machine-readable (read: easily-parsed, if parsing be necessary) and to
+ * highlight the differences between this version of the LEAP protocol and
+ * the baseline version. A client may thus determine whether or not the
+ * running viewer supports some recent feature of interest.
+ *
+ * This method is defined at the top of this implementation file so it's easy
+ * to find, easy to spot, easy to update as we enhance the LEAP protocol.
+ */
+/*static*/ LLSD LLLeapListener::getFeatures()
+{
+ static LLSD features;
+ if (features.isUndefined())
+ {
+ features = LLSD::emptyMap();
+
+ // This initial implementation IS the baseline LEAP protocol; thus the
+ // set of differences is empty; thus features is initially empty.
+// features["featurename"] = "value";
+ }
+
+ return features;
+}
+
+LLLeapListener::LLLeapListener(const ConnectFunc& connect):
+ // Each LEAP plugin has an instance of this listener. Make the command
+ // pump name difficult for other such plugins to guess.
+ LLEventAPI(LLUUID::generateNewID().asString(),
+ "Operations relating to the LLSD Event API Plugin (LEAP) protocol"),
+ mConnect(connect)
+{
+ LLSD need_name(LLSDMap("name", LLSD()));
+ add("newpump",
+ "Instantiate a new LLEventPump named like [\"name\"] and listen to it.\n"
+ "If [\"type\"] == \"LLEventQueue\", make LLEventQueue, else LLEventStream.\n"
+ "Events sent through new LLEventPump will be decorated with [\"pump\"]=name.\n"
+ "Returns actual name in [\"name\"] (may be different if collision).",
+ &LLLeapListener::newpump,
+ need_name);
+ add("killpump",
+ "Delete LLEventPump [\"name\"] created by \"newpump\".\n"
+ "Returns [\"status\"] boolean indicating whether such a pump existed.",
+ &LLLeapListener::killpump,
+ need_name);
+ LLSD need_source_listener(LLSDMap("source", LLSD())("listener", LLSD()));
+ add("listen",
+ "Listen to an existing LLEventPump named [\"source\"], with listener name\n"
+ "[\"listener\"].\n"
+ "By default, send events on [\"source\"] to the plugin, decorated\n"
+ "with [\"pump\"]=[\"source\"].\n"
+ "If [\"dest\"] specified, send undecorated events on [\"source\"] to the\n"
+ "LLEventPump named [\"dest\"].\n"
+ "Returns [\"status\"] boolean indicating whether the connection was made.",
+ &LLLeapListener::listen,
+ need_source_listener);
+ add("stoplistening",
+ "Disconnect a connection previously established by \"listen\".\n"
+ "Pass same [\"source\"] and [\"listener\"] arguments.\n"
+ "Returns [\"status\"] boolean indicating whether such a listener existed.",
+ &LLLeapListener::stoplistening,
+ need_source_listener);
+ add("ping",
+ "No arguments, just a round-trip sanity check.",
+ &LLLeapListener::ping);
+ add("getAPIs",
+ "Enumerate all LLEventAPI instances by name and description.",
+ &LLLeapListener::getAPIs);
+ add("getAPI",
+ "Get name, description, dispatch key and operations for LLEventAPI [\"api\"].",
+ &LLLeapListener::getAPI,
+ LLSD().with("api", LLSD()));
+ add("getFeatures",
+ "Return an LLSD map of feature strings (deltas from baseline LEAP protocol)",
+ static_cast<void (LLLeapListener::*)(const LLSD&) const>(&LLLeapListener::getFeatures));
+ add("getFeature",
+ "Return the feature value with key [\"feature\"]",
+ &LLLeapListener::getFeature,
+ LLSD().with("feature", LLSD()));
+}
+
+LLLeapListener::~LLLeapListener()
+{
+ // We'd have stored a map of LLTempBoundListener instances, save that the
+ // operation of inserting into a std::map necessarily copies the
+ // value_type, and Bad Things would happen if you copied an
+ // LLTempBoundListener. (Destruction of the original would disconnect the
+ // listener, invalidating every stored connection.)
+ BOOST_FOREACH(ListenersMap::value_type& pair, mListeners)
+ {
+ pair.second.disconnect();
+ }
+}
+
+void LLLeapListener::newpump(const LLSD& request)
+{
+ Response reply(LLSD(), request);
+
+ std::string name = request["name"];
+ LLSD const & type = request["type"];
+
+ LLEventPump * new_pump = NULL;
+ if (type.asString() == "LLEventQueue")
+ {
+ new_pump = new LLEventQueue(name, true); // tweak name for uniqueness
+ }
+ else
+ {
+ if (! (type.isUndefined() || type.asString() == "LLEventStream"))
+ {
+ reply.warn(STRINGIZE("unknown 'type' " << type << ", using LLEventStream"));
+ }
+ new_pump = new LLEventStream(name, true); // tweak name for uniqueness
+ }
+
+ name = new_pump->getName();
+
+ mEventPumps.insert(name, new_pump);
+
+ // Now listen on this new pump with our plugin listener
+ std::string myname("llleap");
+ saveListener(name, myname, mConnect(*new_pump, myname));
+
+ reply["name"] = name;
+}
+
+void LLLeapListener::killpump(const LLSD& request)
+{
+ Response reply(LLSD(), request);
+
+ std::string name = request["name"];
+ // success == (nonzero number of entries were erased)
+ reply["status"] = bool(mEventPumps.erase(name));
+}
+
+void LLLeapListener::listen(const LLSD& request)
+{
+ Response reply(LLSD(), request);
+
+ std::string source_name = request["source"];
+ std::string dest_name = request["dest"];
+ std::string listener_name = request["listener"];
+
+ LLEventPump & source = LLEventPumps::instance().obtain(source_name);
+
+ reply["status"] = false;
+ if (mListeners.find(ListenersMap::key_type(source_name, listener_name)) == mListeners.end())
+ {
+ try
+ {
+ if (request["dest"].isDefined())
+ {
+ // If we're asked to connect the "source" pump to a
+ // specific "dest" pump, find dest pump and connect it.
+ LLEventPump & dest = LLEventPumps::instance().obtain(dest_name);
+ saveListener(source_name, listener_name,
+ source.listen(listener_name,
+ boost::bind(&LLEventPump::post, &dest, _1)));
+ }
+ else
+ {
+ // "dest" unspecified means to direct events on "source"
+ // to our plugin listener.
+ saveListener(source_name, listener_name, mConnect(source, listener_name));
+ }
+ reply["status"] = true;
+ }
+ catch (const LLEventPump::DupListenerName &)
+ {
+ // pass - status already set to false
+ }
+ }
+}
+
+void LLLeapListener::stoplistening(const LLSD& request)
+{
+ Response reply(LLSD(), request);
+
+ std::string source_name = request["source"];
+ std::string listener_name = request["listener"];
+
+ ListenersMap::iterator finder =
+ mListeners.find(ListenersMap::key_type(source_name, listener_name));
+
+ reply["status"] = false;
+ if(finder != mListeners.end())
+ {
+ reply["status"] = true;
+ finder->second.disconnect();
+ mListeners.erase(finder);
+ }
+}
+
+void LLLeapListener::ping(const LLSD& request) const
+{
+ // do nothing, default reply suffices
+ Response(LLSD(), request);
+}
+
+void LLLeapListener::getAPIs(const LLSD& request) const
+{
+ Response reply(LLSD(), request);
+
+ for (LLEventAPI::instance_iter eai(LLEventAPI::beginInstances()),
+ eaend(LLEventAPI::endInstances());
+ eai != eaend; ++eai)
+ {
+ LLSD info;
+ info["desc"] = eai->getDesc();
+ reply[eai->getName()] = info;
+ }
+}
+
+void LLLeapListener::getAPI(const LLSD& request) const
+{
+ Response reply(LLSD(), request);
+
+ LLEventAPI* found = LLEventAPI::getInstance(request["api"]);
+ if (found)
+ {
+ reply["name"] = found->getName();
+ reply["desc"] = found->getDesc();
+ reply["key"] = found->getDispatchKey();
+ LLSD ops;
+ for (LLEventAPI::const_iterator oi(found->begin()), oend(found->end());
+ oi != oend; ++oi)
+ {
+ ops.append(found->getMetadata(oi->first));
+ }
+ reply["ops"] = ops;
+ }
+}
+
+void LLLeapListener::getFeatures(const LLSD& request) const
+{
+ // Merely constructing and destroying a Response object suffices here.
+ // Giving it a name would only produce fatal 'unreferenced variable'
+ // warnings.
+ Response(getFeatures(), request);
+}
+
+void LLLeapListener::getFeature(const LLSD& request) const
+{
+ Response reply(LLSD(), request);
+
+ LLSD::String feature_name(request["feature"]);
+ LLSD features(getFeatures());
+ if (features[feature_name].isDefined())
+ {
+ reply["feature"] = features[feature_name];
+ }
+}
+
+void LLLeapListener::saveListener(const std::string& pump_name,
+ const std::string& listener_name,
+ const LLBoundListener& listener)
+{
+ mListeners.insert(ListenersMap::value_type(ListenersMap::key_type(pump_name, listener_name),
+ listener));
+}
diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h
new file mode 100755
index 0000000000..2193d81b9e
--- /dev/null
+++ b/indra/llcommon/llleaplistener.h
@@ -0,0 +1,73 @@
+/**
+ * @file llleaplistener.h
+ * @author Nat Goodspeed
+ * @date 2012-03-16
+ * @brief LLEventAPI supporting LEAP plugins
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLLEAPLISTENER_H)
+#define LL_LLLEAPLISTENER_H
+
+#include "lleventapi.h"
+#include <map>
+#include <string>
+#include <boost/function.hpp>
+#include <boost/ptr_container/ptr_map.hpp>
+
+/// Listener class implementing LLLeap query/control operations.
+/// See https://jira.lindenlab.com/jira/browse/DEV-31978.
+class LLLeapListener: public LLEventAPI
+{
+public:
+ /**
+ * Decouple LLLeap by dependency injection. Certain LLLeapListener
+ * operations must be able to cause LLLeap to listen on a specified
+ * LLEventPump with the LLLeap listener that wraps incoming events in an
+ * outer (pump=, data=) map and forwards them to the plugin. Very well,
+ * define the signature for a function that will perform that, and make
+ * our constructor accept such a function.
+ */
+ typedef boost::function<LLBoundListener(LLEventPump&, const std::string& listener)>
+ ConnectFunc;
+ LLLeapListener(const ConnectFunc& connect);
+ ~LLLeapListener();
+
+ static LLSD getFeatures();
+
+private:
+ void newpump(const LLSD&);
+ void killpump(const LLSD&);
+ void listen(const LLSD&);
+ void stoplistening(const LLSD&);
+ void ping(const LLSD&) const;
+ void getAPIs(const LLSD&) const;
+ void getAPI(const LLSD&) const;
+ void getFeatures(const LLSD&) const;
+ void getFeature(const LLSD&) const;
+
+ void saveListener(const std::string& pump_name, const std::string& listener_name,
+ const LLBoundListener& listener);
+
+ ConnectFunc mConnect;
+
+ // In theory, listen() could simply call the relevant LLEventPump's
+ // listen() method, stoplistening() likewise. Lifespan issues make us
+ // capture the LLBoundListener objects: when this object goes away, all
+ // those listeners should be disconnected. But what if the client listens,
+ // stops, listens again on the same LLEventPump with the same listener
+ // name? Merely collecting LLBoundListeners wouldn't adequately track
+ // that. So capture the latest LLBoundListener for this LLEventPump name
+ // and listener name.
+ typedef std::map<std::pair<std::string, std::string>, LLBoundListener> ListenersMap;
+ ListenersMap mListeners;
+ // Similar lifespan reasoning applies to LLEventPumps instantiated by
+ // newpump() operations.
+ typedef boost::ptr_map<std::string, LLEventPump> EventPumpsMap;
+ EventPumpsMap mEventPumps;
+};
+
+#endif /* ! defined(LL_LLLEAPLISTENER_H) */
diff --git a/indra/llcommon/lllinkedqueue.h b/indra/llcommon/lllinkedqueue.h
deleted file mode 100644
index 3f0eb18b85..0000000000
--- a/indra/llcommon/lllinkedqueue.h
+++ /dev/null
@@ -1,315 +0,0 @@
-/**
- * @file lllinkedqueue.h
- * @brief Declaration of linked queue classes.
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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/lllistenerwrapper.h b/indra/llcommon/lllistenerwrapper.h
index 2f747fb182..09d074abca 100644..100755
--- a/indra/llcommon/lllistenerwrapper.h
+++ b/indra/llcommon/lllistenerwrapper.h
@@ -4,8 +4,25 @@
* @date 2009-11-30
* @brief Introduce LLListenerWrapper template
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llliveappconfig.cpp b/indra/llcommon/llliveappconfig.cpp
index 75bdfee8b7..a9b1cdf4f6 100644..100755
--- a/indra/llcommon/llliveappconfig.cpp
+++ b/indra/llcommon/llliveappconfig.cpp
@@ -2,31 +2,25 @@
* @file llliveappconfig.cpp
* @brief Configuration information for an LLApp that overrides indra.xml
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -53,24 +47,24 @@ LLLiveAppConfig::~LLLiveAppConfig()
// virtual
bool LLLiveAppConfig::loadFile()
{
- llinfos << "LLLiveAppConfig::loadFile(): reading from "
- << filename() << llendl;
- llifstream file(filename());
+ LL_INFOS() << "LLLiveAppConfig::loadFile(): reading from "
+ << filename() << LL_ENDL;
+ llifstream file(filename().c_str());
LLSD config;
if (file.is_open())
{
LLSDSerialize::fromXML(config, file);
if(!config.isMap())
{
- llwarns << "Live app config not an map in " << filename()
- << " Ignoring the data." << llendl;
+ LL_WARNS() << "Live app config not an map in " << filename()
+ << " Ignoring the data." << LL_ENDL;
return false;
}
file.close();
}
else
{
- llinfos << "Live file " << filename() << " does not exit." << llendl;
+ LL_INFOS() << "Live file " << filename() << " does not exit." << LL_ENDL;
}
// *NOTE: we do not handle the else case here because we would not
// have attempted to load the file unless LLLiveFile had
diff --git a/indra/llcommon/llliveappconfig.h b/indra/llcommon/llliveappconfig.h
index 73b3a23352..4fd7c26a07 100644..100755
--- a/indra/llcommon/llliveappconfig.h
+++ b/indra/llcommon/llliveappconfig.h
@@ -2,31 +2,25 @@
* @file llliveappconfig.h
* @brief Configuration information for an LLApp that overrides indra.xml
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp
index 5ca90d82ba..ea485c2d86 100644..100755
--- a/indra/llcommon/lllivefile.cpp
+++ b/indra/llcommon/lllivefile.cpp
@@ -1,31 +1,25 @@
/**
* @file lllivefile.cpp
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -57,6 +51,8 @@ public:
bool mLastExists;
LLEventTimer* mEventTimer;
+private:
+ LOG_CLASS(LLLiveFile);
};
LLLiveFile::Impl::Impl(const std::string& filename, const F32 refresh_period)
@@ -89,46 +85,51 @@ LLLiveFile::~LLLiveFile()
bool LLLiveFile::Impl::check()
{
- 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, &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)
- {
- mLastExists = false;
- return true; // no longer existing is a change!
- }
- return false;
- }
-
- // The file exists, decide if we want to load it.
- if (mLastExists)
+ bool detected_change = false;
+ // Skip the check if not enough time has elapsed and we're not
+ // forcing a check of the file
+ if (mForceCheck || mRefreshTimer.getElapsedTimeF32() >= mRefreshPeriod)
{
- // 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;
- mLastStatTime = stat_data.st_mtime;
- return true;
+ mForceCheck = false; // force only forces one check
+ mRefreshTimer.reset(); // don't check again until mRefreshPeriod has passed
+
+ // Stat the file to see if it exists and when it was last modified.
+ llstat stat_data;
+ if (LLFile::stat(mFilename, &stat_data))
+ {
+ // Couldn't stat the file, that means it doesn't exist or is
+ // broken somehow.
+ if (mLastExists)
+ {
+ mLastExists = false;
+ detected_change = true; // no longer existing is a change!
+ LL_DEBUGS() << "detected deleted file '" << mFilename << "'" << LL_ENDL;
+ }
+ }
+ else
+ {
+ // The file exists
+ if ( ! mLastExists )
+ {
+ // last check, it did not exist - that counts as a change
+ LL_DEBUGS() << "detected created file '" << mFilename << "'" << LL_ENDL;
+ detected_change = true;
+ }
+ else if ( stat_data.st_mtime > mLastModTime )
+ {
+ // file modification time is newer than last check
+ LL_DEBUGS() << "detected updated file '" << mFilename << "'" << LL_ENDL;
+ detected_change = true;
+ }
+ mLastExists = true;
+ mLastStatTime = stat_data.st_mtime;
+ }
+ }
+ if (detected_change)
+ {
+ LL_INFOS() << "detected file change '" << mFilename << "'" << LL_ENDL;
+ }
+ return detected_change;
}
void LLLiveFile::Impl::changed()
diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h
index 2453d7a125..320aa4bc3e 100644..100755
--- a/indra/llcommon/lllivefile.h
+++ b/indra/llcommon/lllivefile.h
@@ -2,31 +2,25 @@
* @file lllivefile.h
* @brief Automatically reloads a file whenever it changes or is removed.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lllocalidhashmap.h b/indra/llcommon/lllocalidhashmap.h
deleted file mode 100644
index a58ae2e7e4..0000000000
--- a/indra/llcommon/lllocalidhashmap.h
+++ /dev/null
@@ -1,901 +0,0 @@
-/**
- * @file lllocalidhashmap.h
- * @brief Map specialized for dealing with local ids
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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/lllog.cpp b/indra/llcommon/lllog.cpp
deleted file mode 100644
index db5d25d5e7..0000000000
--- a/indra/llcommon/lllog.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * @file lllog.cpp
- * @author Don
- * @date 2007-11-27
- * @brief Class to log messages to syslog for streambase to process.
- *
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "lllog.h"
-
-#include "llapp.h"
-#include "llsd.h"
-#include "llsdserialize.h"
-
-
-class LLLogImpl
-{
-public:
- LLLogImpl(LLApp* app) : mApp(app) {}
- ~LLLogImpl() {}
-
- void log(const std::string &message, LLSD& info);
- bool useLegacyLogMessage(const std::string &message);
-
-private:
- LLApp* mApp;
-};
-
-
-//@brief Function to log a message to syslog for streambase to collect.
-void LLLogImpl::log(const std::string &message, LLSD& info)
-{
- static S32 sequence = 0;
- LLSD log_config = mApp->getOption("log-messages");
- if (log_config.has(message))
- {
- LLSD message_config = log_config[message];
- if (message_config.has("use-syslog"))
- {
- if (! message_config["use-syslog"].asBoolean())
- {
- return;
- }
- }
- }
- llinfos << "LLLOGMESSAGE (" << (sequence++) << ") " << message
- << " " << LLSDNotationStreamer(info) << llendl;
-}
-
-//@brief Function to check if specified legacy log message should be sent.
-bool LLLogImpl::useLegacyLogMessage(const std::string &message)
-{
- LLSD log_config = mApp->getOption("log-messages");
- if (log_config.has(message))
- {
- LLSD message_config = log_config[message];
- if (message_config.has("use-legacy"))
- {
- return message_config["use-legacy"].asBoolean();
- }
- }
- return true;
-}
-
-
-LLLog::LLLog(LLApp* app)
-{
- mImpl = new LLLogImpl(app);
-}
-
-LLLog::~LLLog()
-{
- delete mImpl;
- mImpl = NULL;
-}
-
-void LLLog::log(const std::string &message, LLSD& info)
-{
- if (mImpl) mImpl->log(message, info);
-}
-
-bool LLLog::useLegacyLogMessage(const std::string &message)
-{
- if (mImpl)
- {
- return mImpl->useLegacyLogMessage(message);
- }
- return true;
-}
-
diff --git a/indra/llcommon/lllog.h b/indra/llcommon/lllog.h
deleted file mode 100644
index 4b6777bb9c..0000000000
--- a/indra/llcommon/lllog.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * @file lllog.h
- * @author Don
- * @date 2007-11-27
- * @brief Class to log messages to syslog for streambase to process.
- *
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLLOG_H
-#define LL_LLLOG_H
-
-#include <string>
-
-class LLLogImpl;
-class LLApp;
-class LLSD;
-
-class LL_COMMON_API LLLog
-{
-public:
- LLLog(LLApp* app);
- virtual ~LLLog();
-
- virtual void log(const std::string &message, LLSD& info);
- virtual bool useLegacyLogMessage(const std::string &message);
-
-private:
- LLLogImpl* mImpl;
-};
-
-#endif /* LL_LLLOG_H */
-
diff --git a/indra/llcommon/lllslconstants.h b/indra/llcommon/lllslconstants.h
deleted file mode 100644
index 78f4435ed7..0000000000
--- a/indra/llcommon/lllslconstants.h
+++ /dev/null
@@ -1,219 +0,0 @@
-/**
- * @file lllslconstants.h
- * @author James Cook
- * @brief Constants used in lsl.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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 U32 AGENT_AUTOPILOT = 0x2000;
-
-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_GLOW = 25;
-
-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_TYPE_SCULPT = 7;
-
-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_PRIM_SCULPT_TYPE_SPHERE = 1;
-const S32 LSL_PRIM_SCULPT_TYPE_TORUS = 2;
-const S32 LSL_PRIM_SCULPT_TYPE_PLANE = 3;
-const S32 LSL_PRIM_SCULPT_TYPE_CYLINDER = 4;
-const S32 LSL_PRIM_SCULPT_TYPE_MASK = 7;
-const S32 LSL_PRIM_SCULPT_FLAG_INVERT = 64;
-const S32 LSL_PRIM_SCULPT_FLAG_MIRROR = 128;
-
-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;
-
-const S32 STRING_TRIM_HEAD = 0x01;
-const S32 STRING_TRIM_TAIL = 0x02;
-const S32 STRING_TRIM = STRING_TRIM_HEAD | STRING_TRIM_TAIL;
-
-// llGetObjectDetails
-const S32 OBJECT_UNKNOWN_DETAIL = -1;
-const S32 OBJECT_NAME = 1;
-const S32 OBJECT_DESC = 2;
-const S32 OBJECT_POS = 3;
-const S32 OBJECT_ROT = 4;
-const S32 OBJECT_VELOCITY = 5;
-const S32 OBJECT_OWNER = 6;
-const S32 OBJECT_GROUP = 7;
-const S32 OBJECT_CREATOR = 8;
-
-// llTextBox() magic token string - yes this is a hack. sue me.
-const std::string TEXTBOX_MAGIC_TOKEN = "!!llTextBox!!";
-
-// changed() event flags
-const U32 CHANGED_NONE = 0x0;
-const U32 CHANGED_INVENTORY = 0x1;
-const U32 CHANGED_COLOR = 0x2;
-const U32 CHANGED_SHAPE = 0x4;
-const U32 CHANGED_SCALE = 0x8;
-const U32 CHANGED_TEXTURE = 0x10;
-const U32 CHANGED_LINK = 0x20;
-const U32 CHANGED_ALLOWED_DROP = 0x40;
-const U32 CHANGED_OWNER = 0x80;
-const U32 CHANGED_REGION = 0x100;
-const U32 CHANGED_TELEPORT = 0x200;
-const U32 CHANGED_REGION_START = 0x400;
-const U32 CHANGED_MEDIA = 0x800;
-
-// Possible error results
-const U32 LSL_STATUS_OK = 0;
-const U32 LSL_STATUS_MALFORMED_PARAMS = 1000;
-const U32 LSL_STATUS_TYPE_MISMATCH = 1001;
-const U32 LSL_STATUS_BOUNDS_ERROR = 1002;
-const U32 LSL_STATUS_NOT_FOUND = 1003;
-const U32 LSL_STATUS_NOT_SUPPORTED = 1004;
-const U32 LSL_STATUS_INTERNAL_ERROR = 1999;
-
-// Start per-function errors below, starting at 2000:
-const U32 LSL_STATUS_WHITELIST_FAILED = 2001;
-
-#endif
diff --git a/indra/llcommon/llmap.h b/indra/llcommon/llmap.h
deleted file mode 100644
index c94d4d4b2b..0000000000
--- a/indra/llcommon/llmap.h
+++ /dev/null
@@ -1,251 +0,0 @@
-/**
- * @file llmap.h
- * @brief LLMap class header file
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLMAP_H
-#define LL_LLMAP_H
-
-// 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/llmd5.cpp b/indra/llcommon/llmd5.cpp
index da9cb94e13..f942a976b7 100644..100755
--- a/indra/llcommon/llmd5.cpp
+++ b/indra/llcommon/llmd5.cpp
@@ -1,31 +1,25 @@
/**
* @file llmd5.cpp
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -82,7 +76,6 @@ documentation and/or software.
#include "llmd5.h"
-#include <cassert>
#include <iostream> // cerr
// how many bytes to grab at a time when checking files
@@ -125,6 +118,12 @@ void LLMD5::update (const uint1 *input, const uint4 input_length) {
buffer_space = 64 - buffer_index; // how much space is left in buffer
+ // now, transform each 64-byte piece of the input, bypassing the buffer
+ if (input == NULL || input_length == 0){
+ std::cerr << "LLMD5::update: Invalid input!" << std::endl;
+ return;
+ }
+
// Transform as many times as possible.
if (input_length >= buffer_space) { // ie. we have enough to fill the buffer
// fill the rest of the buffer and transform
@@ -134,12 +133,6 @@ void LLMD5::update (const uint1 *input, const uint4 input_length) {
buffer_space);
transform (buffer);
- // now, transform each 64-byte piece of the input, bypassing the buffer
- if (input == NULL || input_length == 0){
- std::cerr << "LLMD5::update: Invalid input!" << std::endl;
- return;
- }
-
for (input_index = buffer_space; input_index + 63 < input_length;
input_index += 64)
transform (input+input_index);
@@ -171,11 +164,6 @@ void LLMD5::update(FILE* file){
}
-
-
-
-
-
// MD5 update for istreams.
// Like update for files; see above.
@@ -186,15 +174,16 @@ void LLMD5::update(std::istream& stream){
while (stream.good()){
stream.read( (char*)buffer, BLOCK_LEN); /* Flawfinder: ignore */ // note that return value of read is unusable.
- len=stream.gcount();
+ len=(int)stream.gcount();
update(buffer, len);
}
}
-
-
-
+void LLMD5::update(const std::string& s)
+{
+ update((unsigned char *)s.c_str(),s.length());
+}
// MD5 finalization. Ends an MD5 message-digest operation, writing the
// the message digest and zeroizing the context.
@@ -277,7 +266,7 @@ LLMD5::LLMD5(const unsigned char *s)
finalize();
}
-void LLMD5::raw_digest(unsigned char *s)
+void LLMD5::raw_digest(unsigned char *s) const
{
if (!finalized)
{
@@ -293,7 +282,7 @@ void LLMD5::raw_digest(unsigned char *s)
-void LLMD5::hex_digest(char *s)
+void LLMD5::hex_digest(char *s) const
{
int i;
@@ -319,6 +308,7 @@ void LLMD5::hex_digest(char *s)
+
std::ostream& operator<<(std::ostream &stream, LLMD5 context)
{
char s[33]; /* Flawfinder: ignore */
@@ -327,13 +317,25 @@ std::ostream& operator<<(std::ostream &stream, LLMD5 context)
return stream;
}
+bool operator==(const LLMD5& a, const LLMD5& b)
+{
+ unsigned char a_guts[16];
+ unsigned char b_guts[16];
+ a.raw_digest(a_guts);
+ b.raw_digest(b_guts);
+ if (memcmp(a_guts,b_guts,16)==0)
+ return true;
+ else
+ return false;
+}
-
+bool operator!=(const LLMD5& a, const LLMD5& b)
+{
+ return !(a==b);
+}
// PRIVATE METHODS:
-
-
void LLMD5::init(){
finalized=0; // we just started!
@@ -531,3 +533,5 @@ void LLMD5::decode (uint4 *output, const uint1 *input, const uint4 len){
output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) |
(((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24);
}
+
+
diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h
index df9d7324ab..1526e6ac3c 100644..100755
--- a/indra/llcommon/llmd5.h
+++ b/indra/llcommon/llmd5.h
@@ -1,31 +1,25 @@
/**
* @file llmd5.h
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -95,6 +89,7 @@ public:
void update (const uint1 *input, const uint4 input_length);
void update (std::istream& stream);
void update (FILE *file);
+ void update (const std::string& str);
void finalize ();
// constructors for special circumstances. All these constructors finalize
@@ -105,11 +100,10 @@ public:
LLMD5 (const unsigned char *string, const unsigned int number);
// methods to acquire finalized result
- void raw_digest(unsigned char *array); // provide 16-byte array for binary data
- void hex_digest(char *string); // provide 33-byte array for ascii-hex string
- friend std::ostream& operator<< (std::ostream&, LLMD5 context);
-
+ void raw_digest(unsigned char *array) const; // provide 16-byte array for binary data
+ void hex_digest(char *string) const; // provide 33-byte array for ascii-hex string
+ friend LL_COMMON_API std::ostream& operator<< (std::ostream&, LLMD5 context);
private:
@@ -131,4 +125,7 @@ private:
};
+LL_COMMON_API bool operator==(const LLMD5& a, const LLMD5& b);
+LL_COMMON_API bool operator!=(const LLMD5& a, const LLMD5& b);
+
#endif // LL_LLMD5_H
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index 2a8015e26d..ae11988df8 100644..100755
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -2,38 +2,34 @@
* @file llmemory.cpp
* @brief Very special memory allocation/deallocation stuff here
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
+
+#include "llthread.h"
+
#if defined(LL_WINDOWS)
-# include <windows.h>
# include <psapi.h>
#elif defined(LL_DARWIN)
# include <sys/types.h>
@@ -45,10 +41,42 @@
#include "llmemory.h"
+#include "llsys.h"
+#include "llframetimer.h"
+#include "lltrace.h"
//----------------------------------------------------------------------------
//static
char* LLMemory::reserveMem = 0;
+U32Kilobytes LLMemory::sAvailPhysicalMemInKB(U32_MAX);
+U32Kilobytes LLMemory::sMaxPhysicalMemInKB(0);
+static LLTrace::SampleStatHandle<F64Megabytes> sAllocatedMem("allocated_mem", "active memory in use by application");
+static LLTrace::SampleStatHandle<F64Megabytes> sVirtualMem("virtual_mem", "virtual memory assigned to application");
+U32Kilobytes LLMemory::sAllocatedMemInKB(0);
+U32Kilobytes LLMemory::sAllocatedPageSizeInKB(0);
+U32Kilobytes LLMemory::sMaxHeapSizeInKB(U32_MAX);
+BOOL LLMemory::sEnableMemoryFailurePrevention = FALSE;
+
+#if __DEBUG_PRIVATE_MEM__
+LLPrivateMemoryPoolManager::mem_allocation_info_t LLPrivateMemoryPoolManager::sMemAllocationTracker;
+#endif
+
+void ll_assert_aligned_func(uintptr_t ptr,U32 alignment)
+{
+#if defined(LL_WINDOWS) && defined(LL_DEBUG_BUFFER_OVERRUN)
+ //do not check
+ return;
+#else
+ #ifdef SHOW_ASSERT
+ // Redundant, place to set breakpoints.
+ if (ptr%alignment!=0)
+ {
+ LL_WARNS() << "alignment check failed" << LL_ENDL;
+ }
+ llassert(ptr%alignment==0);
+ #endif
+#endif
+}
//static
void LLMemory::initClass()
@@ -73,24 +101,162 @@ void LLMemory::freeReserve()
reserveMem = NULL;
}
-void* ll_allocate (size_t size)
+//static
+void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_failure)
+{
+ sMaxHeapSizeInKB = max_heap_size;
+ sEnableMemoryFailurePrevention = prevent_heap_failure ;
+}
+
+//static
+void LLMemory::updateMemoryInfo()
+{
+#if LL_WINDOWS
+ HANDLE self = GetCurrentProcess();
+ PROCESS_MEMORY_COUNTERS counters;
+
+ if (!GetProcessMemoryInfo(self, &counters, sizeof(counters)))
+ {
+ LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL;
+ return ;
+ }
+
+ sAllocatedMemInKB = (U32Bytes)(counters.WorkingSetSize) ;
+ sample(sAllocatedMem, sAllocatedMemInKB);
+ sAllocatedPageSizeInKB = (U32Bytes)(counters.PagefileUsage) ;
+ sample(sVirtualMem, sAllocatedPageSizeInKB);
+
+ U32Kilobytes avail_phys, avail_virtual;
+ LLMemoryInfo::getAvailableMemoryKB(avail_phys, avail_virtual) ;
+ sMaxPhysicalMemInKB = llmin(avail_phys + sAllocatedMemInKB, sMaxHeapSizeInKB);
+
+ if(sMaxPhysicalMemInKB > sAllocatedMemInKB)
+ {
+ sAvailPhysicalMemInKB = sMaxPhysicalMemInKB - sAllocatedMemInKB ;
+ }
+ else
+ {
+ sAvailPhysicalMemInKB = U32Kilobytes(0);
+ }
+#else
+ //not valid for other systems for now.
+ sAllocatedMemInKB = (U32Bytes)LLMemory::getCurrentRSS();
+ sMaxPhysicalMemInKB = (U32Bytes)U32_MAX ;
+ sAvailPhysicalMemInKB = (U32Bytes)U32_MAX ;
+#endif
+
+ return ;
+}
+
+//
+//this function is to test if there is enough space with the size in the virtual address space.
+//it does not do any real allocation
+//if success, it returns the address where the memory chunk can fit in;
+//otherwise it returns NULL.
+//
+//static
+void* LLMemory::tryToAlloc(void* address, U32 size)
{
- if (size == 0)
+#if LL_WINDOWS
+ address = VirtualAlloc(address, size, MEM_RESERVE | MEM_TOP_DOWN, PAGE_NOACCESS) ;
+ if(address)
+ {
+ if(!VirtualFree(address, 0, MEM_RELEASE))
+ {
+ LL_ERRS() << "error happens when free some memory reservation." << LL_ENDL ;
+ }
+ }
+ return address ;
+#else
+ return (void*)0x01 ; //skip checking
+#endif
+}
+
+//static
+void LLMemory::logMemoryInfo(BOOL update)
+{
+ if(update)
+ {
+ updateMemoryInfo() ;
+ LLPrivateMemoryPoolManager::getInstance()->updateStatistics() ;
+ }
+
+ LL_INFOS() << "Current allocated physical memory(KB): " << sAllocatedMemInKB << LL_ENDL ;
+ LL_INFOS() << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << LL_ENDL ;
+ LL_INFOS() << "Current availabe physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ;
+ LL_INFOS() << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << LL_ENDL ;
+
+ LL_INFOS() << "--- private pool information -- " << LL_ENDL ;
+ LL_INFOS() << "Total reserved (KB): " << LLPrivateMemoryPoolManager::getInstance()->mTotalReservedSize / 1024 << LL_ENDL ;
+ LL_INFOS() << "Total allocated (KB): " << LLPrivateMemoryPoolManager::getInstance()->mTotalAllocatedSize / 1024 << LL_ENDL ;
+}
+
+//return 0: everything is normal;
+//return 1: the memory pool is low, but not in danger;
+//return -1: the memory pool is in danger, is about to crash.
+//static
+bool LLMemory::isMemoryPoolLow()
+{
+ static const U32Megabytes LOW_MEMORY_POOL_THRESHOLD(64);
+ const static U32Megabytes MAX_SIZE_CHECKED_MEMORY_BLOCK(64);
+ static void* last_reserved_address = NULL ;
+
+ if(!sEnableMemoryFailurePrevention)
+ {
+ return false ; //no memory failure prevention.
+ }
+
+ if(sAvailPhysicalMemInKB < (LOW_MEMORY_POOL_THRESHOLD / 4)) //out of physical memory
{
- llwarns << "Null allocation" << llendl;
+ return true ;
}
- void *p = malloc(size);
- if (p == NULL)
+
+ if(sAllocatedPageSizeInKB + (LOW_MEMORY_POOL_THRESHOLD / 4) > sMaxHeapSizeInKB) //out of virtual address space.
+ {
+ return true ;
+ }
+
+ bool is_low = (S32)(sAvailPhysicalMemInKB < LOW_MEMORY_POOL_THRESHOLD
+ || sAllocatedPageSizeInKB + LOW_MEMORY_POOL_THRESHOLD > sMaxHeapSizeInKB) ;
+
+ //check the virtual address space fragmentation
+ if(!is_low)
{
- LLMemory::freeReserve();
- llerrs << "Out of memory Error" << llendl;
+ if(!last_reserved_address)
+ {
+ last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK.value()) ;
+ }
+ else
+ {
+ last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK.value()) ;
+ if(!last_reserved_address) //failed, try once more
+ {
+ last_reserved_address = LLMemory::tryToAlloc(last_reserved_address, MAX_SIZE_CHECKED_MEMORY_BLOCK.value()) ;
+ }
+ }
+
+ is_low = !last_reserved_address ; //allocation failed
}
- return p;
+
+ return is_low ;
}
-void ll_release (void *p)
+//static
+U32Kilobytes LLMemory::getAvailableMemKB()
{
- free(p);
+ return sAvailPhysicalMemInKB ;
+}
+
+//static
+U32Kilobytes LLMemory::getMaxMemKB()
+{
+ return sMaxPhysicalMemInKB ;
+}
+
+//static
+U32Kilobytes LLMemory::getAllocatedMemKB()
+{
+ return sAllocatedMemInKB ;
}
//----------------------------------------------------------------------------
@@ -104,13 +270,27 @@ U64 LLMemory::getCurrentRSS()
if (!GetProcessMemoryInfo(self, &counters, sizeof(counters)))
{
- llwarns << "GetProcessMemoryInfo failed" << llendl;
+ LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL;
return 0;
}
return counters.WorkingSetSize;
}
+//static
+U32 LLMemory::getWorkingSetSize()
+{
+ PROCESS_MEMORY_COUNTERS pmc ;
+ U32 ret = 0 ;
+
+ if (GetProcessMemoryInfo( GetCurrentProcess(), &pmc, sizeof(pmc)) )
+ {
+ ret = pmc.WorkingSetSize ;
+ }
+
+ return ret ;
+}
+
#elif defined(LL_DARWIN)
/*
@@ -128,7 +308,7 @@ U64 LLMemory::getCurrentRSS()
// if (sysctl(ctl, 2, &page_size, &size, NULL, 0) == -1)
// {
-// llwarns << "Couldn't get page size" << llendl;
+// LL_WARNS() << "Couldn't get page size" << LL_ENDL;
// return 0;
// } else {
// return page_size;
@@ -147,16 +327,21 @@ U64 LLMemory::getCurrentRSS()
// If we ever wanted it, the process virtual size is also available as:
// virtualSize = basicInfo.virtual_size;
-// llinfos << "resident size is " << residentSize << llendl;
+// LL_INFOS() << "resident size is " << residentSize << LL_ENDL;
}
else
{
- llwarns << "task_info failed" << llendl;
+ LL_WARNS() << "task_info failed" << LL_ENDL;
}
return residentSize;
}
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#elif defined(LL_LINUX)
U64 LLMemory::getCurrentRSS()
@@ -167,7 +352,7 @@ U64 LLMemory::getCurrentRSS()
if (fp == NULL)
{
- llwarns << "couldn't open " << statPath << llendl;
+ LL_WARNS() << "couldn't open " << statPath << LL_ENDL;
goto bail;
}
@@ -180,7 +365,7 @@ U64 LLMemory::getCurrentRSS()
&rss);
if (ret != 1)
{
- llwarns << "couldn't parse contents of " << statPath << llendl;
+ LL_WARNS() << "couldn't parse contents of " << statPath << LL_ENDL;
rss = 0;
}
}
@@ -191,6 +376,11 @@ bail:
return rss;
}
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#elif LL_SOLARIS
#include <sys/types.h>
#include <sys/stat.h>
@@ -205,12 +395,12 @@ U64 LLMemory::getCurrentRSS()
sprintf(path, "/proc/%d/psinfo", (int)getpid());
int proc_fd = -1;
if((proc_fd = open(path, O_RDONLY)) == -1){
- llwarns << "LLmemory::getCurrentRSS() unable to open " << path << ". Returning 0 RSS!" << llendl;
+ LL_WARNS() << "LLmemory::getCurrentRSS() unable to open " << path << ". Returning 0 RSS!" << LL_ENDL;
return 0;
}
psinfo_t proc_psinfo;
if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
- llwarns << "LLmemory::getCurrentRSS() Unable to read from " << path << ". Returning 0 RSS!" << llendl;
+ LL_WARNS() << "LLmemory::getCurrentRSS() Unable to read from " << path << ". Returning 0 RSS!" << LL_ENDL;
close(proc_fd);
return 0;
}
@@ -219,6 +409,12 @@ U64 LLMemory::getCurrentRSS()
return((U64)proc_psinfo.pr_rssize * 1024);
}
+
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0 ;
+}
+
#else
U64 LLMemory::getCurrentRSS()
@@ -226,4 +422,1791 @@ U64 LLMemory::getCurrentRSS()
return 0;
}
+U32 LLMemory::getWorkingSetSize()
+{
+ return 0;
+}
+
+#endif
+
+//--------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------
+//minimum slot size and minimal slot size interval
+const U32 ATOMIC_MEM_SLOT = 16 ; //bytes
+
+//minimum block sizes (page size) for small allocation, medium allocation, large allocation
+const U32 MIN_BLOCK_SIZES[LLPrivateMemoryPool::SUPER_ALLOCATION] = {2 << 10, 4 << 10, 16 << 10} ; //
+
+//maximum block sizes for small allocation, medium allocation, large allocation
+const U32 MAX_BLOCK_SIZES[LLPrivateMemoryPool::SUPER_ALLOCATION] = {64 << 10, 1 << 20, 4 << 20} ;
+
+//minimum slot sizes for small allocation, medium allocation, large allocation
+const U32 MIN_SLOT_SIZES[LLPrivateMemoryPool::SUPER_ALLOCATION] = {ATOMIC_MEM_SLOT, 2 << 10, 512 << 10};
+
+//maximum slot sizes for small allocation, medium allocation, large allocation
+const U32 MAX_SLOT_SIZES[LLPrivateMemoryPool::SUPER_ALLOCATION] = {(2 << 10) - ATOMIC_MEM_SLOT, (512 - 2) << 10, 4 << 20};
+
+//size of a block with multiple slots can not exceed CUT_OFF_SIZE
+const U32 CUT_OFF_SIZE = (64 << 10) ; //64 KB
+
+//max number of slots in a block
+const U32 MAX_NUM_SLOTS_IN_A_BLOCK = llmin(MIN_BLOCK_SIZES[0] / ATOMIC_MEM_SLOT, ATOMIC_MEM_SLOT * 8) ;
+
+//-------------------------------------------------------------
+//align val to be integer times of ATOMIC_MEM_SLOT
+U32 align(U32 val)
+{
+ U32 aligned = (val / ATOMIC_MEM_SLOT) * ATOMIC_MEM_SLOT ;
+ if(aligned < val)
+ {
+ aligned += ATOMIC_MEM_SLOT ;
+ }
+
+ return aligned ;
+}
+
+//-------------------------------------------------------------
+//class LLPrivateMemoryPool::LLMemoryBlock
+//-------------------------------------------------------------
+//
+//each memory block could fit for two page sizes: 0.75 * mSlotSize, which starts from the beginning of the memory chunk and grow towards the end of the
+//the block; another is mSlotSize, which starts from the end of the block and grows towards the beginning of the block.
+//
+LLPrivateMemoryPool::LLMemoryBlock::LLMemoryBlock()
+{
+ //empty
+}
+
+LLPrivateMemoryPool::LLMemoryBlock::~LLMemoryBlock()
+{
+ //empty
+}
+
+//create and initialize a memory block
+void LLPrivateMemoryPool::LLMemoryBlock::init(char* buffer, U32 buffer_size, U32 slot_size)
+{
+ mBuffer = buffer ;
+ mBufferSize = buffer_size ;
+ mSlotSize = slot_size ;
+ mTotalSlots = buffer_size / mSlotSize ;
+
+ llassert_always(buffer_size / mSlotSize <= MAX_NUM_SLOTS_IN_A_BLOCK) ; //max number is 128
+
+ mAllocatedSlots = 0 ;
+ mDummySize = 0 ;
+
+ //init the bit map.
+ //mark free bits
+ if(mTotalSlots > 32) //reserve extra space from mBuffer to store bitmap if needed.
+ {
+ mDummySize = ATOMIC_MEM_SLOT ;
+ mTotalSlots -= (mDummySize + mSlotSize - 1) / mSlotSize ;
+ mUsageBits = 0 ;
+
+ S32 usage_bit_len = (mTotalSlots + 31) / 32 ;
+
+ for(S32 i = 0 ; i < usage_bit_len - 1 ; i++)
+ {
+ *((U32*)mBuffer + i) = 0 ;
+ }
+ for(S32 i = usage_bit_len - 1 ; i < mDummySize / sizeof(U32) ; i++)
+ {
+ *((U32*)mBuffer + i) = 0xffffffff ;
+ }
+
+ if(mTotalSlots & 31)
+ {
+ *((U32*)mBuffer + usage_bit_len - 2) = (0xffffffff << (mTotalSlots & 31)) ;
+ }
+ }
+ else//no extra bitmap space reserved
+ {
+ mUsageBits = 0 ;
+ if(mTotalSlots & 31)
+ {
+ mUsageBits = (0xffffffff << (mTotalSlots & 31)) ;
+ }
+ }
+
+ mSelf = this ;
+ mNext = NULL ;
+ mPrev = NULL ;
+
+ llassert_always(mTotalSlots > 0) ;
+}
+
+//mark this block to be free with the memory [mBuffer, mBuffer + mBufferSize).
+void LLPrivateMemoryPool::LLMemoryBlock::setBuffer(char* buffer, U32 buffer_size)
+{
+ mBuffer = buffer ;
+ mBufferSize = buffer_size ;
+ mSelf = NULL ;
+ mTotalSlots = 0 ; //set the block is free.
+}
+
+//reserve a slot
+char* LLPrivateMemoryPool::LLMemoryBlock::allocate()
+{
+ llassert_always(mAllocatedSlots < mTotalSlots) ;
+
+ //find a free slot
+ U32* bits = NULL ;
+ U32 k = 0 ;
+ if(mUsageBits != 0xffffffff)
+ {
+ bits = &mUsageBits ;
+ }
+ else if(mDummySize > 0)//go to extra space
+ {
+ for(S32 i = 0 ; i < mDummySize / sizeof(U32); i++)
+ {
+ if(*((U32*)mBuffer + i) != 0xffffffff)
+ {
+ bits = (U32*)mBuffer + i ;
+ k = i + 1 ;
+ break ;
+ }
+ }
+ }
+ S32 idx = 0 ;
+ U32 tmp = *bits ;
+ for(; tmp & 1 ; tmp >>= 1, idx++) ;
+
+ //set the slot reserved
+ if(!idx)
+ {
+ *bits |= 1 ;
+ }
+ else
+ {
+ *bits |= (1 << idx) ;
+ }
+
+ mAllocatedSlots++ ;
+
+ return mBuffer + mDummySize + (k * 32 + idx) * mSlotSize ;
+}
+
+//free a slot
+void LLPrivateMemoryPool::LLMemoryBlock::freeMem(void* addr)
+{
+ //bit index
+ U32 idx = ((U32)addr - (U32)mBuffer - mDummySize) / mSlotSize ;
+
+ U32* bits = &mUsageBits ;
+ if(idx >= 32)
+ {
+ bits = (U32*)mBuffer + (idx - 32) / 32 ;
+ }
+
+ //reset the bit
+ if(idx & 31)
+ {
+ *bits &= ~(1 << (idx & 31)) ;
+ }
+ else
+ {
+ *bits &= ~1 ;
+ }
+
+ mAllocatedSlots-- ;
+}
+
+//for debug use: reset the entire bitmap.
+void LLPrivateMemoryPool::LLMemoryBlock::resetBitMap()
+{
+ for(S32 i = 0 ; i < mDummySize / sizeof(U32) ; i++)
+ {
+ *((U32*)mBuffer + i) = 0 ;
+ }
+ mUsageBits = 0 ;
+}
+//-------------------------------------------------------------------
+//class LLMemoryChunk
+//--------------------------------------------------------------------
+LLPrivateMemoryPool::LLMemoryChunk::LLMemoryChunk()
+{
+ //empty
+}
+
+LLPrivateMemoryPool::LLMemoryChunk::~LLMemoryChunk()
+{
+ //empty
+}
+
+//create and init a memory chunk
+void LLPrivateMemoryPool::LLMemoryChunk::init(char* buffer, U32 buffer_size, U32 min_slot_size, U32 max_slot_size, U32 min_block_size, U32 max_block_size)
+{
+ mBuffer = buffer ;
+ mBufferSize = buffer_size ;
+ mAlloatedSize = 0 ;
+
+ mMetaBuffer = mBuffer + sizeof(LLMemoryChunk) ;
+
+ mMinBlockSize = min_block_size; //page size
+ mMinSlotSize = min_slot_size;
+ mMaxSlotSize = max_slot_size ;
+ mBlockLevels = mMaxSlotSize / mMinSlotSize ;
+ mPartitionLevels = max_block_size / mMinBlockSize + 1 ;
+
+ S32 max_num_blocks = (buffer_size - sizeof(LLMemoryChunk) - mBlockLevels * sizeof(LLMemoryBlock*) - mPartitionLevels * sizeof(LLMemoryBlock*)) /
+ (mMinBlockSize + sizeof(LLMemoryBlock)) ;
+ //meta data space
+ mBlocks = (LLMemoryBlock*)mMetaBuffer ; //space reserved for all memory blocks.
+ mAvailBlockList = (LLMemoryBlock**)((char*)mBlocks + sizeof(LLMemoryBlock) * max_num_blocks) ;
+ mFreeSpaceList = (LLMemoryBlock**)((char*)mAvailBlockList + sizeof(LLMemoryBlock*) * mBlockLevels) ;
+
+ //data buffer, which can be used for allocation
+ mDataBuffer = (char*)mFreeSpaceList + sizeof(LLMemoryBlock*) * mPartitionLevels ;
+
+ //alignmnet
+ mDataBuffer = mBuffer + align(mDataBuffer - mBuffer) ;
+
+ //init
+ for(U32 i = 0 ; i < mBlockLevels; i++)
+ {
+ mAvailBlockList[i] = NULL ;
+ }
+ for(U32 i = 0 ; i < mPartitionLevels ; i++)
+ {
+ mFreeSpaceList[i] = NULL ;
+ }
+
+ //assign the entire chunk to the first block
+ mBlocks[0].mPrev = NULL ;
+ mBlocks[0].mNext = NULL ;
+ mBlocks[0].setBuffer(mDataBuffer, buffer_size - (mDataBuffer - mBuffer)) ;
+ addToFreeSpace(&mBlocks[0]) ;
+
+ mNext = NULL ;
+ mPrev = NULL ;
+}
+
+//static
+U32 LLPrivateMemoryPool::LLMemoryChunk::getMaxOverhead(U32 data_buffer_size, U32 min_slot_size,
+ U32 max_slot_size, U32 min_block_size, U32 max_block_size)
+{
+ //for large allocations, reserve some extra memory for meta data to avoid wasting much
+ if(data_buffer_size / min_slot_size < 64) //large allocations
+ {
+ U32 overhead = sizeof(LLMemoryChunk) + (data_buffer_size / min_block_size) * sizeof(LLMemoryBlock) +
+ sizeof(LLMemoryBlock*) * (max_slot_size / min_slot_size) + sizeof(LLMemoryBlock*) * (max_block_size / min_block_size + 1) ;
+
+ //round to integer times of min_block_size
+ overhead = ((overhead + min_block_size - 1) / min_block_size) * min_block_size ;
+ return overhead ;
+ }
+ else
+ {
+ return 0 ; //do not reserve extra overhead if for small allocations
+ }
+}
+
+char* LLPrivateMemoryPool::LLMemoryChunk::allocate(U32 size)
+{
+ if(mMinSlotSize > size)
+ {
+ size = mMinSlotSize ;
+ }
+ if(mAlloatedSize + size > mBufferSize - (mDataBuffer - mBuffer))
+ {
+ return NULL ; //no enough space in this chunk.
+ }
+
+ char* p = NULL ;
+ U32 blk_idx = getBlockLevel(size);
+
+ LLMemoryBlock* blk = NULL ;
+
+ //check if there is free block available
+ if(mAvailBlockList[blk_idx])
+ {
+ blk = mAvailBlockList[blk_idx] ;
+ p = blk->allocate() ;
+
+ if(blk->isFull())
+ {
+ popAvailBlockList(blk_idx) ;
+ }
+ }
+
+ //ask for a new block
+ if(!p)
+ {
+ blk = addBlock(blk_idx) ;
+ if(blk)
+ {
+ p = blk->allocate() ;
+
+ if(blk->isFull())
+ {
+ popAvailBlockList(blk_idx) ;
+ }
+ }
+ }
+
+ //ask for space from larger blocks
+ if(!p)
+ {
+ for(S32 i = blk_idx + 1 ; i < mBlockLevels; i++)
+ {
+ if(mAvailBlockList[i])
+ {
+ blk = mAvailBlockList[i] ;
+ p = blk->allocate() ;
+
+ if(blk->isFull())
+ {
+ popAvailBlockList(i) ;
+ }
+ break ;
+ }
+ }
+ }
+
+ if(p && blk)
+ {
+ mAlloatedSize += blk->getSlotSize() ;
+ }
+ return p ;
+}
+
+void LLPrivateMemoryPool::LLMemoryChunk::freeMem(void* addr)
+{
+ U32 blk_idx = getPageIndex((U32)addr) ;
+ LLMemoryBlock* blk = (LLMemoryBlock*)(mMetaBuffer + blk_idx * sizeof(LLMemoryBlock)) ;
+ blk = blk->mSelf ;
+
+ bool was_full = blk->isFull() ;
+ blk->freeMem(addr) ;
+ mAlloatedSize -= blk->getSlotSize() ;
+
+ if(blk->empty())
+ {
+ removeBlock(blk) ;
+ }
+ else if(was_full)
+ {
+ addToAvailBlockList(blk) ;
+ }
+}
+
+bool LLPrivateMemoryPool::LLMemoryChunk::empty()
+{
+ return !mAlloatedSize ;
+}
+
+bool LLPrivateMemoryPool::LLMemoryChunk::containsAddress(const char* addr) const
+{
+ return (U32)mBuffer <= (U32)addr && (U32)mBuffer + mBufferSize > (U32)addr ;
+}
+
+//debug use
+void LLPrivateMemoryPool::LLMemoryChunk::dump()
+{
+#if 0
+ //sanity check
+ //for(S32 i = 0 ; i < mBlockLevels ; i++)
+ //{
+ // LLMemoryBlock* blk = mAvailBlockList[i] ;
+ // while(blk)
+ // {
+ // blk_list.push_back(blk) ;
+ // blk = blk->mNext ;
+ // }
+ //}
+ for(S32 i = 0 ; i < mPartitionLevels ; i++)
+ {
+ LLMemoryBlock* blk = mFreeSpaceList[i] ;
+ while(blk)
+ {
+ blk_list.push_back(blk) ;
+ blk = blk->mNext ;
+ }
+ }
+
+ std::sort(blk_list.begin(), blk_list.end(), LLMemoryBlock::CompareAddress());
+
+ U32 total_size = blk_list[0]->getBufferSize() ;
+ for(U32 i = 1 ; i < blk_list.size(); i++)
+ {
+ total_size += blk_list[i]->getBufferSize() ;
+ if((U32)blk_list[i]->getBuffer() < (U32)blk_list[i-1]->getBuffer() + blk_list[i-1]->getBufferSize())
+ {
+ LL_ERRS() << "buffer corrupted." << LL_ENDL ;
+ }
+ }
+
+ llassert_always(total_size + mMinBlockSize >= mBufferSize - ((U32)mDataBuffer - (U32)mBuffer)) ;
+
+ U32 blk_num = (mBufferSize - (mDataBuffer - mBuffer)) / mMinBlockSize ;
+ for(U32 i = 0 ; i < blk_num ; )
+ {
+ LLMemoryBlock* blk = &mBlocks[i] ;
+ if(blk->mSelf)
+ {
+ U32 end = blk->getBufferSize() / mMinBlockSize ;
+ for(U32 j = 0 ; j < end ; j++)
+ {
+ llassert_always(blk->mSelf == blk || !blk->mSelf) ;
+ }
+ i += end ;
+ }
+ else
+ {
+ LL_ERRS() << "gap happens" << LL_ENDL ;
+ }
+ }
+#endif
+#if 0
+ LL_INFOS() << "---------------------------" << LL_ENDL ;
+ LL_INFOS() << "Chunk buffer: " << (U32)getBuffer() << " size: " << getBufferSize() << LL_ENDL ;
+
+ LL_INFOS() << "available blocks ... " << LL_ENDL ;
+ for(S32 i = 0 ; i < mBlockLevels ; i++)
+ {
+ LLMemoryBlock* blk = mAvailBlockList[i] ;
+ while(blk)
+ {
+ LL_INFOS() << "blk buffer " << (U32)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ;
+ blk = blk->mNext ;
+ }
+ }
+
+ LL_INFOS() << "free blocks ... " << LL_ENDL ;
+ for(S32 i = 0 ; i < mPartitionLevels ; i++)
+ {
+ LLMemoryBlock* blk = mFreeSpaceList[i] ;
+ while(blk)
+ {
+ LL_INFOS() << "blk buffer " << (U32)blk->getBuffer() << " size: " << blk->getBufferSize() << LL_ENDL ;
+ blk = blk->mNext ;
+ }
+ }
+#endif
+}
+
+//compute the size for a block, the size is round to integer times of mMinBlockSize.
+U32 LLPrivateMemoryPool::LLMemoryChunk::calcBlockSize(U32 slot_size)
+{
+ //
+ //Note: we try to make a block to have 32 slots if the size is not over 32 pages
+ //32 is the number of bits of an integer in a 32-bit system
+ //
+
+ U32 block_size;
+ U32 cut_off_size = llmin(CUT_OFF_SIZE, (U32)(mMinBlockSize << 5)) ;
+
+ if((slot_size << 5) <= mMinBlockSize)//for small allocations, return one page
+ {
+ block_size = mMinBlockSize ;
+ }
+ else if(slot_size >= cut_off_size)//for large allocations, return one-slot block
+ {
+ block_size = (slot_size / mMinBlockSize) * mMinBlockSize ;
+ if(block_size < slot_size)
+ {
+ block_size += mMinBlockSize ;
+ }
+ }
+ else //medium allocations
+ {
+ if((slot_size << 5) >= cut_off_size)
+ {
+ block_size = cut_off_size ;
+ }
+ else
+ {
+ block_size = ((slot_size << 5) / mMinBlockSize) * mMinBlockSize ;
+ }
+ }
+
+ llassert_always(block_size >= slot_size) ;
+
+ return block_size ;
+}
+
+//create a new block in the chunk
+LLPrivateMemoryPool::LLMemoryBlock* LLPrivateMemoryPool::LLMemoryChunk::addBlock(U32 blk_idx)
+{
+ U32 slot_size = mMinSlotSize * (blk_idx + 1) ;
+ U32 preferred_block_size = calcBlockSize(slot_size) ;
+ U16 idx = getPageLevel(preferred_block_size);
+ LLMemoryBlock* blk = NULL ;
+
+ if(mFreeSpaceList[idx])//if there is free slot for blk_idx
+ {
+ blk = createNewBlock(mFreeSpaceList[idx], preferred_block_size, slot_size, blk_idx) ;
+ }
+ else if(mFreeSpaceList[mPartitionLevels - 1]) //search free pool
+ {
+ blk = createNewBlock(mFreeSpaceList[mPartitionLevels - 1], preferred_block_size, slot_size, blk_idx) ;
+ }
+ else //search for other non-preferred but enough space slot.
+ {
+ S32 min_idx = 0 ;
+ if(slot_size > mMinBlockSize)
+ {
+ min_idx = getPageLevel(slot_size) ;
+ }
+ for(S32 i = (S32)idx - 1 ; i >= min_idx ; i--) //search the small slots first
+ {
+ if(mFreeSpaceList[i])
+ {
+ U32 new_preferred_block_size = mFreeSpaceList[i]->getBufferSize();
+ new_preferred_block_size = (new_preferred_block_size / mMinBlockSize) * mMinBlockSize ; //round to integer times of mMinBlockSize.
+
+ //create a NEW BLOCK THERE.
+ if(new_preferred_block_size >= slot_size) //at least there is space for one slot.
+ {
+
+ blk = createNewBlock(mFreeSpaceList[i], new_preferred_block_size, slot_size, blk_idx) ;
+ }
+ break ;
+ }
+ }
+
+ if(!blk)
+ {
+ for(U16 i = idx + 1 ; i < mPartitionLevels - 1; i++) //search the large slots
+ {
+ if(mFreeSpaceList[i])
+ {
+ //create a NEW BLOCK THERE.
+ blk = createNewBlock(mFreeSpaceList[i], preferred_block_size, slot_size, blk_idx) ;
+ break ;
+ }
+ }
+ }
+ }
+
+ return blk ;
+}
+
+//create a new block at the designed location
+LLPrivateMemoryPool::LLMemoryBlock* LLPrivateMemoryPool::LLMemoryChunk::createNewBlock(LLMemoryBlock* blk, U32 buffer_size, U32 slot_size, U32 blk_idx)
+{
+ //unlink from the free space
+ removeFromFreeSpace(blk) ;
+
+ //check the rest space
+ U32 new_free_blk_size = blk->getBufferSize() - buffer_size ;
+ if(new_free_blk_size < mMinBlockSize) //can not partition the memory into size smaller than mMinBlockSize
+ {
+ new_free_blk_size = 0 ; //discard the last small extra space.
+ }
+
+ //add the rest space back to the free list
+ if(new_free_blk_size > 0) //blk still has free space
+ {
+ LLMemoryBlock* next_blk = blk + (buffer_size / mMinBlockSize) ;
+ next_blk->mPrev = NULL ;
+ next_blk->mNext = NULL ;
+ next_blk->setBuffer(blk->getBuffer() + buffer_size, new_free_blk_size) ;
+ addToFreeSpace(next_blk) ;
+ }
+
+ blk->init(blk->getBuffer(), buffer_size, slot_size) ;
+ //insert to the available block list...
+ mAvailBlockList[blk_idx] = blk ;
+
+ //mark the address map: all blocks covered by this block space pointing back to this block.
+ U32 end = (buffer_size / mMinBlockSize) ;
+ for(U32 i = 1 ; i < end ; i++)
+ {
+ (blk + i)->mSelf = blk ;
+ }
+
+ return blk ;
+}
+
+//delete a block, release the block to the free pool.
+void LLPrivateMemoryPool::LLMemoryChunk::removeBlock(LLMemoryBlock* blk)
+{
+ //remove from the available block list
+ if(blk->mPrev)
+ {
+ blk->mPrev->mNext = blk->mNext ;
+ }
+ if(blk->mNext)
+ {
+ blk->mNext->mPrev = blk->mPrev ;
+ }
+ U32 blk_idx = getBlockLevel(blk->getSlotSize());
+ if(mAvailBlockList[blk_idx] == blk)
+ {
+ mAvailBlockList[blk_idx] = blk->mNext ;
+ }
+
+ blk->mNext = NULL ;
+ blk->mPrev = NULL ;
+
+ //mark it free
+ blk->setBuffer(blk->getBuffer(), blk->getBufferSize()) ;
+
+#if 1
+ //merge blk with neighbors if possible
+ if(blk->getBuffer() > mDataBuffer) //has the left neighbor
+ {
+ if((blk - 1)->mSelf->isFree())
+ {
+ LLMemoryBlock* left_blk = (blk - 1)->mSelf ;
+ removeFromFreeSpace((blk - 1)->mSelf);
+ left_blk->setBuffer(left_blk->getBuffer(), left_blk->getBufferSize() + blk->getBufferSize()) ;
+ blk = left_blk ;
+ }
+ }
+ if(blk->getBuffer() + blk->getBufferSize() <= mBuffer + mBufferSize - mMinBlockSize) //has the right neighbor
+ {
+ U32 d = blk->getBufferSize() / mMinBlockSize ;
+ if((blk + d)->isFree())
+ {
+ LLMemoryBlock* right_blk = blk + d ;
+ removeFromFreeSpace(blk + d) ;
+ blk->setBuffer(blk->getBuffer(), blk->getBufferSize() + right_blk->getBufferSize()) ;
+ }
+ }
+#endif
+
+ addToFreeSpace(blk) ;
+
+ return ;
+}
+
+//the top block in the list is full, pop it out of the list
+void LLPrivateMemoryPool::LLMemoryChunk::popAvailBlockList(U32 blk_idx)
+{
+ if(mAvailBlockList[blk_idx])
+ {
+ LLMemoryBlock* next = mAvailBlockList[blk_idx]->mNext ;
+ if(next)
+ {
+ next->mPrev = NULL ;
+ }
+ mAvailBlockList[blk_idx]->mPrev = NULL ;
+ mAvailBlockList[blk_idx]->mNext = NULL ;
+ mAvailBlockList[blk_idx] = next ;
+ }
+}
+
+//add the block back to the free pool
+void LLPrivateMemoryPool::LLMemoryChunk::addToFreeSpace(LLMemoryBlock* blk)
+{
+ llassert_always(!blk->mPrev) ;
+ llassert_always(!blk->mNext) ;
+
+ U16 free_idx = blk->getBufferSize() / mMinBlockSize - 1;
+
+ (blk + free_idx)->mSelf = blk ; //mark the end pointing back to the head.
+ free_idx = llmin(free_idx, (U16)(mPartitionLevels - 1)) ;
+
+ blk->mNext = mFreeSpaceList[free_idx] ;
+ if(mFreeSpaceList[free_idx])
+ {
+ mFreeSpaceList[free_idx]->mPrev = blk ;
+ }
+ mFreeSpaceList[free_idx] = blk ;
+ blk->mPrev = NULL ;
+ blk->mSelf = blk ;
+
+ return ;
+}
+
+//remove the space from the free pool
+void LLPrivateMemoryPool::LLMemoryChunk::removeFromFreeSpace(LLMemoryBlock* blk)
+{
+ U16 free_idx = blk->getBufferSize() / mMinBlockSize - 1;
+ free_idx = llmin(free_idx, (U16)(mPartitionLevels - 1)) ;
+
+ if(mFreeSpaceList[free_idx] == blk)
+ {
+ mFreeSpaceList[free_idx] = blk->mNext ;
+ }
+ if(blk->mPrev)
+ {
+ blk->mPrev->mNext = blk->mNext ;
+ }
+ if(blk->mNext)
+ {
+ blk->mNext->mPrev = blk->mPrev ;
+ }
+ blk->mNext = NULL ;
+ blk->mPrev = NULL ;
+ blk->mSelf = NULL ;
+
+ return ;
+}
+
+void LLPrivateMemoryPool::LLMemoryChunk::addToAvailBlockList(LLMemoryBlock* blk)
+{
+ llassert_always(!blk->mPrev) ;
+ llassert_always(!blk->mNext) ;
+
+ U32 blk_idx = getBlockLevel(blk->getSlotSize());
+
+ blk->mNext = mAvailBlockList[blk_idx] ;
+ if(blk->mNext)
+ {
+ blk->mNext->mPrev = blk ;
+ }
+ blk->mPrev = NULL ;
+ mAvailBlockList[blk_idx] = blk ;
+
+ return ;
+}
+
+U32 LLPrivateMemoryPool::LLMemoryChunk::getPageIndex(U32 addr)
+{
+ return (addr - (U32)mDataBuffer) / mMinBlockSize ;
+}
+
+//for mAvailBlockList
+U32 LLPrivateMemoryPool::LLMemoryChunk::getBlockLevel(U32 size)
+{
+ llassert(size >= mMinSlotSize && size <= mMaxSlotSize) ;
+
+ //start from 0
+ return (size + mMinSlotSize - 1) / mMinSlotSize - 1 ;
+}
+
+//for mFreeSpaceList
+U16 LLPrivateMemoryPool::LLMemoryChunk::getPageLevel(U32 size)
+{
+ //start from 0
+ U16 level = size / mMinBlockSize - 1 ;
+ if(level >= mPartitionLevels)
+ {
+ level = mPartitionLevels - 1 ;
+ }
+ return level ;
+}
+
+//-------------------------------------------------------------------
+//class LLPrivateMemoryPool
+//--------------------------------------------------------------------
+const U32 CHUNK_SIZE = 4 << 20 ; //4 MB
+const U32 LARGE_CHUNK_SIZE = 4 * CHUNK_SIZE ; //16 MB
+LLPrivateMemoryPool::LLPrivateMemoryPool(S32 type, U32 max_pool_size) :
+ mMutexp(NULL),
+ mReservedPoolSize(0),
+ mHashFactor(1),
+ mType(type),
+ mMaxPoolSize(max_pool_size)
+{
+ if(type == STATIC_THREADED || type == VOLATILE_THREADED)
+ {
+ mMutexp = new LLMutex(NULL) ;
+ }
+
+ for(S32 i = 0 ; i < SUPER_ALLOCATION ; i++)
+ {
+ mChunkList[i] = NULL ;
+ }
+
+ mNumOfChunks = 0 ;
+}
+
+LLPrivateMemoryPool::~LLPrivateMemoryPool()
+{
+ destroyPool();
+ delete mMutexp ;
+}
+
+char* LLPrivateMemoryPool::allocate(U32 size)
+{
+ if(!size)
+ {
+ return NULL ;
+ }
+
+ //if the asked size larger than MAX_BLOCK_SIZE, fetch from heap directly, the pool does not manage it
+ if(size >= CHUNK_SIZE)
+ {
+ return (char*)ll_aligned_malloc_16(size) ;
+ }
+
+ char* p = NULL ;
+
+ //find the appropriate chunk
+ S32 chunk_idx = getChunkIndex(size) ;
+
+ lock() ;
+
+ LLMemoryChunk* chunk = mChunkList[chunk_idx];
+ while(chunk)
+ {
+ if((p = chunk->allocate(size)))
+ {
+ break ;
+ }
+ chunk = chunk->mNext ;
+ }
+
+ //fetch new memory chunk
+ if(!p)
+ {
+ if(mReservedPoolSize + CHUNK_SIZE > mMaxPoolSize)
+ {
+ chunk = mChunkList[chunk_idx];
+ while(chunk)
+ {
+ if((p = chunk->allocate(size)))
+ {
+ break ;
+ }
+ chunk = chunk->mNext ;
+ }
+ }
+ else
+ {
+ chunk = addChunk(chunk_idx) ;
+ if(chunk)
+ {
+ p = chunk->allocate(size) ;
+ }
+ }
+ }
+
+ unlock() ;
+
+ if(!p) //to get memory from the private pool failed, try the heap directly
+ {
+ static bool to_log = true ;
+
+ if(to_log)
+ {
+ LL_WARNS() << "The memory pool overflows, now using heap directly!" << LL_ENDL ;
+ to_log = false ;
+ }
+
+ return (char*)ll_aligned_malloc_16(size) ;
+ }
+
+ return p ;
+}
+
+void LLPrivateMemoryPool::freeMem(void* addr)
+{
+ if(!addr)
+ {
+ return ;
+ }
+
+ lock() ;
+
+ LLMemoryChunk* chunk = findChunk((char*)addr) ;
+
+ if(!chunk)
+ {
+ ll_aligned_free_16(addr) ; //release from heap
+ }
+ else
+ {
+ chunk->freeMem(addr) ;
+
+ if(chunk->empty())
+ {
+ removeChunk(chunk) ;
+ }
+ }
+
+ unlock() ;
+}
+
+void LLPrivateMemoryPool::dump()
+{
+}
+
+U32 LLPrivateMemoryPool::getTotalAllocatedSize()
+{
+ U32 total_allocated = 0 ;
+
+ LLMemoryChunk* chunk ;
+ for(S32 i = 0 ; i < SUPER_ALLOCATION ; i++)
+ {
+ chunk = mChunkList[i];
+ while(chunk)
+ {
+ total_allocated += chunk->getAllocatedSize() ;
+ chunk = chunk->mNext ;
+ }
+ }
+
+ return total_allocated ;
+}
+
+void LLPrivateMemoryPool::lock()
+{
+ if(mMutexp)
+ {
+ mMutexp->lock() ;
+ }
+}
+
+void LLPrivateMemoryPool::unlock()
+{
+ if(mMutexp)
+ {
+ mMutexp->unlock() ;
+ }
+}
+
+S32 LLPrivateMemoryPool::getChunkIndex(U32 size)
+{
+ S32 i ;
+ for(i = 0 ; size > MAX_SLOT_SIZES[i]; i++);
+
+ llassert_always(i < SUPER_ALLOCATION);
+
+ return i ;
+}
+
+//destroy the entire pool
+void LLPrivateMemoryPool::destroyPool()
+{
+ lock() ;
+
+ if(mNumOfChunks > 0)
+ {
+ LL_WARNS() << "There is some memory not freed when destroy the memory pool!" << LL_ENDL ;
+ }
+
+ mNumOfChunks = 0 ;
+ mChunkHashList.clear() ;
+ mHashFactor = 1 ;
+ for(S32 i = 0 ; i < SUPER_ALLOCATION ; i++)
+ {
+ mChunkList[i] = NULL ;
+ }
+
+ unlock() ;
+}
+
+bool LLPrivateMemoryPool::checkSize(U32 asked_size)
+{
+ if(mReservedPoolSize + asked_size > mMaxPoolSize)
+ {
+ LL_INFOS() << "Max pool size: " << mMaxPoolSize << LL_ENDL ;
+ LL_INFOS() << "Total reserved size: " << mReservedPoolSize + asked_size << LL_ENDL ;
+ LL_INFOS() << "Total_allocated Size: " << getTotalAllocatedSize() << LL_ENDL ;
+
+ //LL_ERRS() << "The pool is overflowing..." << LL_ENDL ;
+
+ return false ;
+ }
+
+ return true ;
+}
+
+LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::addChunk(S32 chunk_index)
+{
+ U32 preferred_size ;
+ U32 overhead ;
+ if(chunk_index < LARGE_ALLOCATION)
+ {
+ preferred_size = CHUNK_SIZE ; //4MB
+ overhead = LLMemoryChunk::getMaxOverhead(preferred_size, MIN_SLOT_SIZES[chunk_index],
+ MAX_SLOT_SIZES[chunk_index], MIN_BLOCK_SIZES[chunk_index], MAX_BLOCK_SIZES[chunk_index]) ;
+ }
+ else
+ {
+ preferred_size = LARGE_CHUNK_SIZE ; //16MB
+ overhead = LLMemoryChunk::getMaxOverhead(preferred_size, MIN_SLOT_SIZES[chunk_index],
+ MAX_SLOT_SIZES[chunk_index], MIN_BLOCK_SIZES[chunk_index], MAX_BLOCK_SIZES[chunk_index]) ;
+ }
+
+ if(!checkSize(preferred_size + overhead))
+ {
+ return NULL ;
+ }
+
+ mReservedPoolSize += preferred_size + overhead ;
+
+ char* buffer = (char*)ll_aligned_malloc_16(preferred_size + overhead) ;
+ if(!buffer)
+ {
+ return NULL ;
+ }
+
+ LLMemoryChunk* chunk = new (buffer) LLMemoryChunk() ;
+ chunk->init(buffer, preferred_size + overhead, MIN_SLOT_SIZES[chunk_index],
+ MAX_SLOT_SIZES[chunk_index], MIN_BLOCK_SIZES[chunk_index], MAX_BLOCK_SIZES[chunk_index]) ;
+
+ //add to the tail of the linked list
+ {
+ if(!mChunkList[chunk_index])
+ {
+ mChunkList[chunk_index] = chunk ;
+ }
+ else
+ {
+ LLMemoryChunk* cur = mChunkList[chunk_index] ;
+ while(cur->mNext)
+ {
+ cur = cur->mNext ;
+ }
+ cur->mNext = chunk ;
+ chunk->mPrev = cur ;
+ }
+ }
+
+ //insert into the hash table
+ addToHashTable(chunk) ;
+
+ mNumOfChunks++;
+
+ return chunk ;
+}
+
+void LLPrivateMemoryPool::removeChunk(LLMemoryChunk* chunk)
+{
+ if(!chunk)
+ {
+ return ;
+ }
+
+ //remove from the linked list
+ for(S32 i = 0 ; i < SUPER_ALLOCATION ; i++)
+ {
+ if(mChunkList[i] == chunk)
+ {
+ mChunkList[i] = chunk->mNext ;
+ }
+ }
+
+ if(chunk->mPrev)
+ {
+ chunk->mPrev->mNext = chunk->mNext ;
+ }
+ if(chunk->mNext)
+ {
+ chunk->mNext->mPrev = chunk->mPrev ;
+ }
+
+ //remove from the hash table
+ removeFromHashTable(chunk) ;
+
+ mNumOfChunks--;
+ mReservedPoolSize -= chunk->getBufferSize() ;
+
+ //release memory
+ ll_aligned_free_16(chunk->getBuffer()) ;
+}
+
+U16 LLPrivateMemoryPool::findHashKey(const char* addr)
+{
+ return (((U32)addr) / CHUNK_SIZE) % mHashFactor ;
+}
+
+LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::findChunk(const char* addr)
+{
+ U16 key = findHashKey(addr) ;
+ if(mChunkHashList.size() <= key)
+ {
+ return NULL ;
+ }
+
+ return mChunkHashList[key].findChunk(addr) ;
+}
+
+void LLPrivateMemoryPool::addToHashTable(LLMemoryChunk* chunk)
+{
+ static const U16 HASH_FACTORS[] = {41, 83, 193, 317, 419, 523, 719, 997, 1523, 0xFFFF};
+
+ U16 i ;
+ if(mChunkHashList.empty())
+ {
+ mHashFactor = HASH_FACTORS[0] ;
+ rehash() ;
+ }
+
+ U16 start_key = findHashKey(chunk->getBuffer()) ;
+ U16 end_key = findHashKey(chunk->getBuffer() + chunk->getBufferSize() - 1) ;
+ bool need_rehash = false ;
+
+ if(mChunkHashList[start_key].hasElement(chunk))
+ {
+ return; //already inserted.
+ }
+ need_rehash = mChunkHashList[start_key].add(chunk) ;
+
+ if(start_key == end_key && !need_rehash)
+ {
+ return ; //done
+ }
+
+ if(!need_rehash)
+ {
+ need_rehash = mChunkHashList[end_key].add(chunk) ;
+ }
+
+ if(!need_rehash)
+ {
+ if(end_key < start_key)
+ {
+ need_rehash = fillHashTable(start_key + 1, mHashFactor, chunk) ;
+ if(!need_rehash)
+ {
+ need_rehash = fillHashTable(0, end_key, chunk) ;
+ }
+ }
+ else
+ {
+ need_rehash = fillHashTable(start_key + 1, end_key, chunk) ;
+ }
+ }
+
+ if(need_rehash)
+ {
+ i = 0 ;
+ while(HASH_FACTORS[i] <= mHashFactor) i++;
+
+ mHashFactor = HASH_FACTORS[i] ;
+ llassert_always(mHashFactor != 0xFFFF) ;//stop point to prevent endlessly recursive calls
+
+ rehash() ;
+ }
+}
+
+void LLPrivateMemoryPool::removeFromHashTable(LLMemoryChunk* chunk)
+{
+ U16 start_key = findHashKey(chunk->getBuffer()) ;
+ U16 end_key = findHashKey(chunk->getBuffer() + chunk->getBufferSize() - 1) ;
+
+ mChunkHashList[start_key].remove(chunk) ;
+ if(start_key == end_key)
+ {
+ return ; //done
+ }
+
+ mChunkHashList[end_key].remove(chunk) ;
+
+ if(end_key < start_key)
+ {
+ for(U16 i = start_key + 1 ; i < mHashFactor; i++)
+ {
+ mChunkHashList[i].remove(chunk) ;
+ }
+ for(U16 i = 0 ; i < end_key; i++)
+ {
+ mChunkHashList[i].remove(chunk) ;
+ }
+ }
+ else
+ {
+ for(U16 i = start_key + 1 ; i < end_key; i++)
+ {
+ mChunkHashList[i].remove(chunk) ;
+ }
+ }
+}
+
+void LLPrivateMemoryPool::rehash()
+{
+ LL_INFOS() << "new hash factor: " << mHashFactor << LL_ENDL ;
+
+ mChunkHashList.clear() ;
+ mChunkHashList.resize(mHashFactor) ;
+
+ LLMemoryChunk* chunk ;
+ for(U16 i = 0 ; i < SUPER_ALLOCATION ; i++)
+ {
+ chunk = mChunkList[i] ;
+ while(chunk)
+ {
+ addToHashTable(chunk) ;
+ chunk = chunk->mNext ;
+ }
+ }
+}
+
+bool LLPrivateMemoryPool::fillHashTable(U16 start, U16 end, LLMemoryChunk* chunk)
+{
+ for(U16 i = start; i < end; i++)
+ {
+ if(mChunkHashList[i].add(chunk))
+ {
+ return true ;
+ }
+ }
+
+ return false ;
+}
+
+//--------------------------------------------------------------------
+// class LLChunkHashElement
+//--------------------------------------------------------------------
+LLPrivateMemoryPool::LLMemoryChunk* LLPrivateMemoryPool::LLChunkHashElement::findChunk(const char* addr)
+{
+ if(mFirst && mFirst->containsAddress(addr))
+ {
+ return mFirst ;
+ }
+ else if(mSecond && mSecond->containsAddress(addr))
+ {
+ return mSecond ;
+ }
+
+ return NULL ;
+}
+
+//return false if successfully inserted to the hash slot.
+bool LLPrivateMemoryPool::LLChunkHashElement::add(LLPrivateMemoryPool::LLMemoryChunk* chunk)
+{
+ llassert_always(!hasElement(chunk)) ;
+
+ if(!mFirst)
+ {
+ mFirst = chunk ;
+ }
+ else if(!mSecond)
+ {
+ mSecond = chunk ;
+ }
+ else
+ {
+ return true ; //failed
+ }
+
+ return false ;
+}
+
+void LLPrivateMemoryPool::LLChunkHashElement::remove(LLPrivateMemoryPool::LLMemoryChunk* chunk)
+{
+ if(mFirst == chunk)
+ {
+ mFirst = NULL ;
+ }
+ else if(mSecond ==chunk)
+ {
+ mSecond = NULL ;
+ }
+ else
+ {
+ LL_ERRS() << "This slot does not contain this chunk!" << LL_ENDL ;
+ }
+}
+
+//--------------------------------------------------------------------
+//class LLPrivateMemoryPoolManager
+//--------------------------------------------------------------------
+LLPrivateMemoryPoolManager* LLPrivateMemoryPoolManager::sInstance = NULL ;
+BOOL LLPrivateMemoryPoolManager::sPrivatePoolEnabled = FALSE ;
+std::vector<LLPrivateMemoryPool*> LLPrivateMemoryPoolManager::sDanglingPoolList ;
+
+LLPrivateMemoryPoolManager::LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size)
+{
+ mPoolList.resize(LLPrivateMemoryPool::MAX_TYPES) ;
+
+ for(S32 i = 0 ; i < LLPrivateMemoryPool::MAX_TYPES; i++)
+ {
+ mPoolList[i] = NULL ;
+ }
+
+ sPrivatePoolEnabled = enabled ;
+
+ const U32 MAX_POOL_SIZE = 256 * 1024 * 1024 ; //256 MB
+ mMaxPrivatePoolSize = llmax(max_pool_size, MAX_POOL_SIZE) ;
+}
+
+LLPrivateMemoryPoolManager::~LLPrivateMemoryPoolManager()
+{
+
+#if __DEBUG_PRIVATE_MEM__
+ if(!sMemAllocationTracker.empty())
+ {
+ LL_WARNS() << "there is potential memory leaking here. The list of not freed memory blocks are from: " <<LL_ENDL ;
+
+ S32 k = 0 ;
+ for(mem_allocation_info_t::iterator iter = sMemAllocationTracker.begin() ; iter != sMemAllocationTracker.end() ; ++iter)
+ {
+ LL_INFOS() << k++ << ", " << (U32)iter->first << " : " << iter->second << LL_ENDL ;
+ }
+ sMemAllocationTracker.clear() ;
+ }
+#endif
+
+#if 0
+ //all private pools should be released by their owners before reaching here.
+ for(S32 i = 0 ; i < LLPrivateMemoryPool::MAX_TYPES; i++)
+ {
+ llassert_always(!mPoolList[i]) ;
+ }
+ mPoolList.clear() ;
+
+#else
+ //forcefully release all memory
+ for(S32 i = 0 ; i < LLPrivateMemoryPool::MAX_TYPES; i++)
+ {
+ if(mPoolList[i])
+ {
+ if(mPoolList[i]->isEmpty())
+ {
+ delete mPoolList[i] ;
+ }
+ else
+ {
+ //can not delete this pool because it has alloacted memory to be freed.
+ //move it to the dangling list.
+ sDanglingPoolList.push_back(mPoolList[i]) ;
+ }
+
+ mPoolList[i] = NULL ;
+ }
+ }
+ mPoolList.clear() ;
+#endif
+}
+
+//static
+void LLPrivateMemoryPoolManager::initClass(BOOL enabled, U32 max_pool_size)
+{
+ llassert_always(!sInstance) ;
+
+ sInstance = new LLPrivateMemoryPoolManager(enabled, max_pool_size) ;
+}
+
+//static
+LLPrivateMemoryPoolManager* LLPrivateMemoryPoolManager::getInstance()
+{
+ //if(!sInstance)
+ //{
+ // sInstance = new LLPrivateMemoryPoolManager(FALSE) ;
+ //}
+ return sInstance ;
+}
+
+//static
+void LLPrivateMemoryPoolManager::destroyClass()
+{
+ if(sInstance)
+ {
+ delete sInstance ;
+ sInstance = NULL ;
+ }
+}
+
+LLPrivateMemoryPool* LLPrivateMemoryPoolManager::newPool(S32 type)
+{
+ if(!sPrivatePoolEnabled)
+ {
+ return NULL ;
+ }
+
+ if(!mPoolList[type])
+ {
+ mPoolList[type] = new LLPrivateMemoryPool(type, mMaxPrivatePoolSize) ;
+ }
+
+ return mPoolList[type] ;
+}
+
+void LLPrivateMemoryPoolManager::deletePool(LLPrivateMemoryPool* pool)
+{
+ if(pool && pool->isEmpty())
+ {
+ mPoolList[pool->getType()] = NULL ;
+ delete pool;
+ }
+}
+
+//debug
+void LLPrivateMemoryPoolManager::updateStatistics()
+{
+ mTotalReservedSize = 0 ;
+ mTotalAllocatedSize = 0 ;
+
+ for(U32 i = 0; i < mPoolList.size(); i++)
+ {
+ if(mPoolList[i])
+ {
+ mTotalReservedSize += mPoolList[i]->getTotalReservedSize() ;
+ mTotalAllocatedSize += mPoolList[i]->getTotalAllocatedSize() ;
+ }
+ }
+}
+
+#if __DEBUG_PRIVATE_MEM__
+//static
+char* LLPrivateMemoryPoolManager::allocate(LLPrivateMemoryPool* poolp, U32 size, const char* function, const int line)
+{
+ char* p ;
+
+ if(!poolp)
+ {
+ p = (char*)ll_aligned_malloc_16(size) ;
+ }
+ else
+ {
+ p = poolp->allocate(size) ;
+ }
+
+ if(p)
+ {
+ char num[16] ;
+ sprintf(num, " line: %d ", line) ;
+ std::string str(function) ;
+ str += num;
+
+ sMemAllocationTracker[p] = str ;
+ }
+
+ return p ;
+}
+#else
+//static
+char* LLPrivateMemoryPoolManager::allocate(LLPrivateMemoryPool* poolp, U32 size)
+{
+ if(poolp)
+ {
+ return poolp->allocate(size) ;
+ }
+ else
+ {
+ return (char*)ll_aligned_malloc_16(size) ;
+ }
+}
+#endif
+
+//static
+void LLPrivateMemoryPoolManager::freeMem(LLPrivateMemoryPool* poolp, void* addr)
+{
+ if(!addr)
+ {
+ return ;
+ }
+
+#if __DEBUG_PRIVATE_MEM__
+ sMemAllocationTracker.erase((char*)addr) ;
+#endif
+
+ if(poolp)
+ {
+ poolp->freeMem(addr) ;
+ }
+ else
+ {
+ if(!sPrivatePoolEnabled)
+ {
+ ll_aligned_free_16(addr) ; //private pool is disabled.
+ }
+ else if(!sInstance) //the private memory manager is destroyed, try the dangling list
+ {
+ for(S32 i = 0 ; i < sDanglingPoolList.size(); i++)
+ {
+ if(sDanglingPoolList[i]->findChunk((char*)addr))
+ {
+ sDanglingPoolList[i]->freeMem(addr) ;
+ if(sDanglingPoolList[i]->isEmpty())
+ {
+ delete sDanglingPoolList[i] ;
+
+ if(i < sDanglingPoolList.size() - 1)
+ {
+ sDanglingPoolList[i] = sDanglingPoolList[sDanglingPoolList.size() - 1] ;
+ }
+ sDanglingPoolList.pop_back() ;
+ }
+
+ addr = NULL ;
+ break ;
+ }
+ }
+ llassert_always(!addr) ; //addr should be release before hitting here!
+ }
+ else
+ {
+ LL_ERRS() << "private pool is used before initialized.!" << LL_ENDL ;
+ }
+ }
+}
+
+//--------------------------------------------------------------------
+//class LLPrivateMemoryPoolTester
+//--------------------------------------------------------------------
+#if 0
+LLPrivateMemoryPoolTester* LLPrivateMemoryPoolTester::sInstance = NULL ;
+LLPrivateMemoryPool* LLPrivateMemoryPoolTester::sPool = NULL ;
+LLPrivateMemoryPoolTester::LLPrivateMemoryPoolTester()
+{
+}
+
+LLPrivateMemoryPoolTester::~LLPrivateMemoryPoolTester()
+{
+}
+
+//static
+LLPrivateMemoryPoolTester* LLPrivateMemoryPoolTester::getInstance()
+{
+ if(!sInstance)
+ {
+ sInstance = ::new LLPrivateMemoryPoolTester() ;
+ }
+ return sInstance ;
+}
+
+//static
+void LLPrivateMemoryPoolTester::destroy()
+{
+ if(sInstance)
+ {
+ ::delete sInstance ;
+ sInstance = NULL ;
+ }
+
+ if(sPool)
+ {
+ LLPrivateMemoryPoolManager::getInstance()->deletePool(sPool) ;
+ sPool = NULL ;
+ }
+}
+
+void LLPrivateMemoryPoolTester::run(S32 type)
+{
+ if(sPool)
+ {
+ LLPrivateMemoryPoolManager::getInstance()->deletePool(sPool) ;
+ }
+ sPool = LLPrivateMemoryPoolManager::getInstance()->newPool(type) ;
+
+ //run the test
+ correctnessTest() ;
+ performanceTest() ;
+ //fragmentationtest() ;
+
+ //release pool.
+ LLPrivateMemoryPoolManager::getInstance()->deletePool(sPool) ;
+ sPool = NULL ;
+}
+
+void LLPrivateMemoryPoolTester::test(U32 min_size, U32 max_size, U32 stride, U32 times,
+ bool random_deletion, bool output_statistics)
+{
+ U32 levels = (max_size - min_size) / stride + 1 ;
+ char*** p ;
+ U32 i, j ;
+ U32 total_allocated_size = 0 ;
+
+ //allocate space for p ;
+ if(!(p = ::new char**[times]) || !(*p = ::new char*[times * levels]))
+ {
+ LL_ERRS() << "memory initialization for p failed" << LL_ENDL ;
+ }
+
+ //init
+ for(i = 0 ; i < times; i++)
+ {
+ p[i] = *p + i * levels ;
+ for(j = 0 ; j < levels; j++)
+ {
+ p[i][j] = NULL ;
+ }
+ }
+
+ //allocation
+ U32 size ;
+ for(i = 0 ; i < times ; i++)
+ {
+ for(j = 0 ; j < levels; j++)
+ {
+ size = min_size + j * stride ;
+ p[i][j] = ALLOCATE_MEM(sPool, size) ;
+
+ total_allocated_size+= size ;
+
+ *(U32*)p[i][j] = i ;
+ *((U32*)p[i][j] + 1) = j ;
+ //p[i][j][size - 1] = '\0' ; //access the last element to verify the success of the allocation.
+
+ //randomly release memory
+ if(random_deletion)
+ {
+ S32 k = rand() % levels ;
+
+ if(p[i][k])
+ {
+ llassert_always(*(U32*)p[i][k] == i && *((U32*)p[i][k] + 1) == k) ;
+ FREE_MEM(sPool, p[i][k]) ;
+ total_allocated_size -= min_size + k * stride ;
+ p[i][k] = NULL ;
+ }
+ }
+ }
+ }
+
+ //output pool allocation statistics
+ if(output_statistics)
+ {
+ }
+
+ //release all memory allocations
+ for(i = 0 ; i < times; i++)
+ {
+ for(j = 0 ; j < levels; j++)
+ {
+ if(p[i][j])
+ {
+ llassert_always(*(U32*)p[i][j] == i && *((U32*)p[i][j] + 1) == j) ;
+ FREE_MEM(sPool, p[i][j]) ;
+ total_allocated_size -= min_size + j * stride ;
+ p[i][j] = NULL ;
+ }
+ }
+ }
+
+ ::delete[] *p ;
+ ::delete[] p ;
+}
+
+void LLPrivateMemoryPoolTester::testAndTime(U32 size, U32 times)
+{
+ LLTimer timer ;
+
+ LL_INFOS() << " -**********************- " << LL_ENDL ;
+ LL_INFOS() << "test size: " << size << " test times: " << times << LL_ENDL ;
+
+ timer.reset() ;
+ char** p = new char*[times] ;
+
+ //using the customized memory pool
+ //allocation
+ for(U32 i = 0 ; i < times; i++)
+ {
+ p[i] = ALLOCATE_MEM(sPool, size) ;
+ if(!p[i])
+ {
+ LL_ERRS() << "allocation failed" << LL_ENDL ;
+ }
+ }
+ //de-allocation
+ for(U32 i = 0 ; i < times; i++)
+ {
+ FREE_MEM(sPool, p[i]) ;
+ p[i] = NULL ;
+ }
+ LL_INFOS() << "time spent using customized memory pool: " << timer.getElapsedTimeF32() << LL_ENDL ;
+
+ timer.reset() ;
+
+ //using the standard allocator/de-allocator:
+ //allocation
+ for(U32 i = 0 ; i < times; i++)
+ {
+ p[i] = ::new char[size] ;
+ if(!p[i])
+ {
+ LL_ERRS() << "allocation failed" << LL_ENDL ;
+ }
+ }
+ //de-allocation
+ for(U32 i = 0 ; i < times; i++)
+ {
+ ::delete[] p[i] ;
+ p[i] = NULL ;
+ }
+ LL_INFOS() << "time spent using standard allocator/de-allocator: " << timer.getElapsedTimeF32() << LL_ENDL ;
+
+ delete[] p;
+}
+
+void LLPrivateMemoryPoolTester::correctnessTest()
+{
+ //try many different sized allocation, and all kinds of edge cases, access the allocated memory
+ //to see if allocation is right.
+
+ //edge case
+ char* p = ALLOCATE_MEM(sPool, 0) ;
+ FREE_MEM(sPool, p) ;
+
+ //small sized
+ // [8 bytes, 2KB), each asks for 256 allocations and deallocations
+ test(8, 2040, 8, 256, true, true) ;
+
+ //medium sized
+ //[2KB, 512KB), each asks for 16 allocations and deallocations
+ test(2048, 512 * 1024 - 2048, 2048, 16, true, true) ;
+
+ //large sized
+ //[512KB, 4MB], each asks for 8 allocations and deallocations
+ test(512 * 1024, 4 * 1024 * 1024, 64 * 1024, 6, true, true) ;
+}
+
+void LLPrivateMemoryPoolTester::performanceTest()
+{
+ U32 test_size[3] = {768, 3* 1024, 3* 1024 * 1024};
+
+ //small sized
+ testAndTime(test_size[0], 8) ;
+
+ //medium sized
+ testAndTime(test_size[1], 8) ;
+
+ //large sized
+ testAndTime(test_size[2], 8) ;
+}
+
+void LLPrivateMemoryPoolTester::fragmentationtest()
+{
+ //for internal fragmentation statistics:
+ //every time when asking for a new chunk during correctness test, and performance test,
+ //print out the chunk usage statistices.
+}
+#endif
+//--------------------------------------------------------------------
+
+#if defined(LL_WINDOWS) && defined(LL_DEBUG_BUFFER_OVERRUN)
+
+#include <map>
+
+struct mem_info {
+ std::map<void*, void*> memory_info;
+ LLMutex mutex;
+
+ static mem_info& get() {
+ static mem_info instance;
+ return instance;
+ }
+
+private:
+ mem_info(){}
+};
+
+void* ll_aligned_malloc_fallback( size_t size, int align )
+{
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+
+ unsigned int for_alloc = sysinfo.dwPageSize;
+ while(for_alloc < size) for_alloc += sysinfo.dwPageSize;
+
+ void *p = VirtualAlloc(NULL, for_alloc+sysinfo.dwPageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ if(NULL == p) {
+ // call debugger
+ __asm int 3;
+ }
+ DWORD old;
+ BOOL Res = VirtualProtect((void*)((char*)p + for_alloc), sysinfo.dwPageSize, PAGE_NOACCESS, &old);
+ if(FALSE == Res) {
+ // call debugger
+ __asm int 3;
+ }
+
+ void* ret = (void*)((char*)p + for_alloc-size);
+
+ {
+ LLMutexLock lock(&mem_info::get().mutex);
+ mem_info::get().memory_info.insert(std::pair<void*, void*>(ret, p));
+ }
+
+
+ return ret;
+}
+
+void ll_aligned_free_fallback( void* ptr )
+{
+ LLMutexLock lock(&mem_info::get().mutex);
+ VirtualFree(mem_info::get().memory_info.find(ptr)->second, 0, MEM_RELEASE);
+ mem_info::get().memory_info.erase(ptr);
+}
+
#endif
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index 1c6f64dd8b..c4c9cc0566 100644..100755
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -1,45 +1,338 @@
-/**
+/**
* @file llmemory.h
* @brief Memory allocation/deallocation header-stuff goes here.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLMEMORY_H
#define LLMEMORY_H
+#include "linden_common.h"
+#include "llunits.h"
+#if !LL_WINDOWS
+#include <stdint.h>
+#endif
+
+class LLMutex ;
+
+#if LL_WINDOWS && LL_DEBUG
+#define LL_CHECK_MEMORY llassert(_CrtCheckMemory());
+#else
+#define LL_CHECK_MEMORY
+#endif
+
+
+#if LL_WINDOWS
+#define LL_ALIGN_OF __alignof
+#else
+#define LL_ALIGN_OF __align_of__
+#endif
+
+#if LL_WINDOWS
+#define LL_DEFAULT_HEAP_ALIGN 8
+#elif LL_DARWIN
+#define LL_DEFAULT_HEAP_ALIGN 16
+#elif LL_LINUX
+#define LL_DEFAULT_HEAP_ALIGN 8
+#endif
+
+
+LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
+
+#ifdef SHOW_ASSERT
+#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(reinterpret_cast<uintptr_t>(ptr),((U32)alignment))
+#else
+#define ll_assert_aligned(ptr,alignment)
+#endif
+
+#include <xmmintrin.h>
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address)
+{
+ return reinterpret_cast<T*>(
+ (reinterpret_cast<uintptr_t>(address) + 0xF) & ~0xF);
+}
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
+{
+ return reinterpret_cast<T*>(
+ (reinterpret_cast<uintptr_t>(address) + 0x3F) & ~0x3F);
+}
+
+#if LL_LINUX || LL_DARWIN
+
+#define LL_ALIGN_PREFIX(x)
+#define LL_ALIGN_POSTFIX(x) __attribute__((aligned(x)))
+
+#elif LL_WINDOWS
+
+#define LL_ALIGN_PREFIX(x) __declspec(align(x))
+#define LL_ALIGN_POSTFIX(x)
+
+#else
+#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined"
+#endif
+
+#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16)
+
+//------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------
+ // for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library
+ // change preprocessro code to: #if 1 && defined(LL_WINDOWS)
+
+#if 0 && defined(LL_WINDOWS)
+ void* ll_aligned_malloc_fallback( size_t size, int align );
+ void ll_aligned_free_fallback( void* ptr );
+//------------------------------------------------------------------------------------------------
+#else
+ inline void* ll_aligned_malloc_fallback( size_t size, int align )
+ {
+ #if defined(LL_WINDOWS)
+ return _aligned_malloc(size, align);
+ #else
+ void* mem = malloc( size + (align - 1) + sizeof(void*) );
+ char* aligned = ((char*)mem) + sizeof(void*);
+ aligned += align - ((uintptr_t)aligned & (align - 1));
+
+ ((void**)aligned)[-1] = mem;
+ return aligned;
+ #endif
+ }
+
+ inline void ll_aligned_free_fallback( void* ptr )
+ {
+ #if defined(LL_WINDOWS)
+ _aligned_free(ptr);
+ #else
+ if (ptr)
+ {
+ free( ((void**)ptr)[-1] );
+ }
+ #endif
+ }
+#endif
+//------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------
+
+#if !LL_USE_TCMALLOC
+inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16().
+{
+#if defined(LL_WINDOWS)
+ return _aligned_malloc(size, 16);
+#elif defined(LL_DARWIN)
+ return malloc(size); // default osx malloc is 16 byte aligned.
+#else
+ void *rtn;
+ if (LL_LIKELY(0 == posix_memalign(&rtn, 16, size)))
+ return rtn;
+ else // bad alignment requested, or out of memory
+ return NULL;
+#endif
+}
+
+inline void ll_aligned_free_16(void *p)
+{
+#if defined(LL_WINDOWS)
+ _aligned_free(p);
+#elif defined(LL_DARWIN)
+ return free(p);
+#else
+ free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+
+inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // returned hunk MUST be freed with ll_aligned_free_16().
+{
+#if defined(LL_WINDOWS)
+ return _aligned_realloc(ptr, size, 16);
+#elif defined(LL_DARWIN)
+ return realloc(ptr,size); // default osx malloc is 16 byte aligned.
+#else
+ //FIXME: memcpy is SLOW
+ void* ret = ll_aligned_malloc_16(size);
+ if (ptr)
+ {
+ if (ret)
+ {
+ // Only copy the size of the smallest memory block to avoid memory corruption.
+ memcpy(ret, ptr, llmin(old_size, size));
+ }
+ ll_aligned_free_16(ptr);
+ }
+ return ret;
+#endif
+}
+
+#else // USE_TCMALLOC
+// ll_aligned_foo_16 are not needed with tcmalloc
+#define ll_aligned_malloc_16 malloc
+#define ll_aligned_realloc_16(a,b,c) realloc(a,b)
+#define ll_aligned_free_16 free
+#endif // USE_TCMALLOC
+
+inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32().
+{
+#if defined(LL_WINDOWS)
+ return _aligned_malloc(size, 32);
+#elif defined(LL_DARWIN)
+ return ll_aligned_malloc_fallback( size, 32 );
+#else
+ void *rtn;
+ if (LL_LIKELY(0 == posix_memalign(&rtn, 32, size)))
+ return rtn;
+ else // bad alignment requested, or out of memory
+ return NULL;
+#endif
+}
+
+inline void ll_aligned_free_32(void *p)
+{
+#if defined(LL_WINDOWS)
+ _aligned_free(p);
+#elif defined(LL_DARWIN)
+ ll_aligned_free_fallback( p );
+#else
+ free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+// general purpose dispatch functions that are forced inline so they can compile down to a single call
+template<size_t ALIGNMENT>
+LL_FORCE_INLINE void* ll_aligned_malloc(size_t size)
+{
+ if (LL_DEFAULT_HEAP_ALIGN % ALIGNMENT == 0)
+ {
+ return malloc(size);
+ }
+ else if (ALIGNMENT == 16)
+ {
+ return ll_aligned_malloc_16(size);
+ }
+ else if (ALIGNMENT == 32)
+ {
+ return ll_aligned_malloc_32(size);
+ }
+ else
+ {
+ return ll_aligned_malloc_fallback(size, ALIGNMENT);
+ }
+}
+
+template<size_t ALIGNMENT>
+LL_FORCE_INLINE void ll_aligned_free(void* ptr)
+{
+ if (ALIGNMENT == LL_DEFAULT_HEAP_ALIGN)
+ {
+ free(ptr);
+ }
+ else if (ALIGNMENT == 16)
+ {
+ ll_aligned_free_16(ptr);
+ }
+ else if (ALIGNMENT == 32)
+ {
+ return ll_aligned_free_32(ptr);
+ }
+ else
+ {
+ return ll_aligned_free_fallback(ptr);
+ }
+}
+
+// Copy words 16-byte blocks from src to dst. Source and destination MUST NOT OVERLAP.
+// Source and dest must be 16-byte aligned and size must be multiple of 16.
+//
+inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __restrict src, size_t bytes)
+{
+ assert(src != NULL);
+ assert(dst != NULL);
+ assert(bytes > 0);
+ assert((bytes % sizeof(F32))== 0);
+ ll_assert_aligned(src,16);
+ ll_assert_aligned(dst,16);
-extern S32 gTotalDAlloc;
-extern S32 gTotalDAUse;
-extern S32 gDACount;
+ assert((src < dst) ? ((src + bytes) <= dst) : ((dst + bytes) <= src));
+ assert(bytes%16==0);
-extern void* ll_allocate (size_t size);
-extern void ll_release (void *p);
+ char* end = dst + bytes;
+
+ if (bytes > 64)
+ {
+
+ // Find start of 64b aligned area within block
+ //
+ void* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst);
+
+ //at least 64 bytes before the end of the destination, switch to 16 byte copies
+ void* end_64 = end-64;
+
+ // Prefetch the head of the 64b area now
+ //
+ _mm_prefetch((char*)begin_64, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA);
+
+ // Copy 16b chunks until we're 64b aligned
+ //
+ while (dst < begin_64)
+ {
+
+ _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src));
+ dst += 16;
+ src += 16;
+ }
+
+ // Copy 64b chunks up to your tail
+ //
+ // might be good to shmoo the 512b prefetch offset
+ // (characterize performance for various values)
+ //
+ while (dst < end_64)
+ {
+ _mm_prefetch((char*)src + 512, _MM_HINT_NTA);
+ _mm_prefetch((char*)dst + 512, _MM_HINT_NTA);
+ _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src));
+ _mm_store_ps((F32*)(dst + 16), _mm_load_ps((F32*)(src + 16)));
+ _mm_store_ps((F32*)(dst + 32), _mm_load_ps((F32*)(src + 32)));
+ _mm_store_ps((F32*)(dst + 48), _mm_load_ps((F32*)(src + 48)));
+ dst += 64;
+ src += 64;
+ }
+ }
+
+ // Copy remainder 16b tail chunks (or ALL 16b chunks for sub-64b copies)
+ //
+ while (dst < end)
+ {
+ _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src));
+ dst += 16;
+ src += 16;
+ }
+}
+
+#ifndef __DEBUG_PRIVATE_MEM__
+#define __DEBUG_PRIVATE_MEM__ 0
+#endif
class LL_COMMON_API LLMemory
{
@@ -50,10 +343,348 @@ public:
// Return the resident set size of the current process, in bytes.
// Return value is zero if not known.
static U64 getCurrentRSS();
+ static U32 getWorkingSetSize();
+ static void* tryToAlloc(void* address, U32 size);
+ static void initMaxHeapSizeGB(F32Gigabytes max_heap_size, BOOL prevent_heap_failure);
+ static void updateMemoryInfo() ;
+ static void logMemoryInfo(BOOL update = FALSE);
+ static bool isMemoryPoolLow();
+
+ static U32Kilobytes getAvailableMemKB() ;
+ static U32Kilobytes getMaxMemKB() ;
+ static U32Kilobytes getAllocatedMemKB() ;
private:
static char* reserveMem;
+ static U32Kilobytes sAvailPhysicalMemInKB ;
+ static U32Kilobytes sMaxPhysicalMemInKB ;
+ static U32Kilobytes sAllocatedMemInKB;
+ static U32Kilobytes sAllocatedPageSizeInKB ;
+
+ static U32Kilobytes sMaxHeapSizeInKB;
+ static BOOL sEnableMemoryFailurePrevention;
+};
+
+//
+//class LLPrivateMemoryPool defines a private memory pool for an application to use, so the application does not
+//need to access the heap directly fro each memory allocation. Throught this, the allocation speed is faster,
+//and reduces virtaul address space gragmentation problem.
+//Note: this class is thread-safe by passing true to the constructor function. However, you do not need to do this unless
+//you are sure the memory allocation and de-allocation will happen in different threads. To make the pool thread safe
+//increases allocation and deallocation cost.
+//
+class LL_COMMON_API LLPrivateMemoryPool
+{
+ friend class LLPrivateMemoryPoolManager ;
+
+public:
+ class LL_COMMON_API LLMemoryBlock //each block is devided into slots uniformly
+ {
+ public:
+ LLMemoryBlock() ;
+ ~LLMemoryBlock() ;
+
+ void init(char* buffer, U32 buffer_size, U32 slot_size) ;
+ void setBuffer(char* buffer, U32 buffer_size) ;
+
+ char* allocate() ;
+ void freeMem(void* addr) ;
+
+ bool empty() {return !mAllocatedSlots;}
+ bool isFull() {return mAllocatedSlots == mTotalSlots;}
+ bool isFree() {return !mTotalSlots;}
+
+ U32 getSlotSize()const {return mSlotSize;}
+ U32 getTotalSlots()const {return mTotalSlots;}
+ U32 getBufferSize()const {return mBufferSize;}
+ char* getBuffer() const {return mBuffer;}
+
+ //debug use
+ void resetBitMap() ;
+ private:
+ char* mBuffer;
+ U32 mSlotSize ; //when the block is not initialized, it is the buffer size.
+ U32 mBufferSize ;
+ U32 mUsageBits ;
+ U8 mTotalSlots ;
+ U8 mAllocatedSlots ;
+ U8 mDummySize ; //size of extra bytes reserved for mUsageBits.
+
+ public:
+ LLMemoryBlock* mPrev ;
+ LLMemoryBlock* mNext ;
+ LLMemoryBlock* mSelf ;
+
+ struct CompareAddress
+ {
+ bool operator()(const LLMemoryBlock* const& lhs, const LLMemoryBlock* const& rhs)
+ {
+ return (U32)lhs->getBuffer() < (U32)rhs->getBuffer();
+ }
+ };
+ };
+
+ class LL_COMMON_API LLMemoryChunk //is divided into memory blocks.
+ {
+ public:
+ LLMemoryChunk() ;
+ ~LLMemoryChunk() ;
+
+ void init(char* buffer, U32 buffer_size, U32 min_slot_size, U32 max_slot_size, U32 min_block_size, U32 max_block_size) ;
+ void setBuffer(char* buffer, U32 buffer_size) ;
+
+ bool empty() ;
+
+ char* allocate(U32 size) ;
+ void freeMem(void* addr) ;
+
+ char* getBuffer() const {return mBuffer;}
+ U32 getBufferSize() const {return mBufferSize;}
+ U32 getAllocatedSize() const {return mAlloatedSize;}
+
+ bool containsAddress(const char* addr) const;
+
+ static U32 getMaxOverhead(U32 data_buffer_size, U32 min_slot_size,
+ U32 max_slot_size, U32 min_block_size, U32 max_block_size) ;
+
+ void dump() ;
+
+ private:
+ U32 getPageIndex(U32 addr) ;
+ U32 getBlockLevel(U32 size) ;
+ U16 getPageLevel(U32 size) ;
+ LLMemoryBlock* addBlock(U32 blk_idx) ;
+ void popAvailBlockList(U32 blk_idx) ;
+ void addToFreeSpace(LLMemoryBlock* blk) ;
+ void removeFromFreeSpace(LLMemoryBlock* blk) ;
+ void removeBlock(LLMemoryBlock* blk) ;
+ void addToAvailBlockList(LLMemoryBlock* blk) ;
+ U32 calcBlockSize(U32 slot_size);
+ LLMemoryBlock* createNewBlock(LLMemoryBlock* blk, U32 buffer_size, U32 slot_size, U32 blk_idx) ;
+
+ private:
+ LLMemoryBlock** mAvailBlockList ;//256 by mMinSlotSize
+ LLMemoryBlock** mFreeSpaceList;
+ LLMemoryBlock* mBlocks ; //index of blocks by address.
+
+ char* mBuffer ;
+ U32 mBufferSize ;
+ char* mDataBuffer ;
+ char* mMetaBuffer ;
+ U32 mMinBlockSize ;
+ U32 mMinSlotSize ;
+ U32 mMaxSlotSize ;
+ U32 mAlloatedSize ;
+ U16 mBlockLevels;
+ U16 mPartitionLevels;
+
+ public:
+ //form a linked list
+ LLMemoryChunk* mNext ;
+ LLMemoryChunk* mPrev ;
+ } ;
+
+private:
+ LLPrivateMemoryPool(S32 type, U32 max_pool_size) ;
+ ~LLPrivateMemoryPool() ;
+
+ char *allocate(U32 size) ;
+ void freeMem(void* addr) ;
+
+ void dump() ;
+ U32 getTotalAllocatedSize() ;
+ U32 getTotalReservedSize() {return mReservedPoolSize;}
+ S32 getType() const {return mType; }
+ bool isEmpty() const {return !mNumOfChunks; }
+
+private:
+ void lock() ;
+ void unlock() ;
+ S32 getChunkIndex(U32 size) ;
+ LLMemoryChunk* addChunk(S32 chunk_index) ;
+ bool checkSize(U32 asked_size) ;
+ void removeChunk(LLMemoryChunk* chunk) ;
+ U16 findHashKey(const char* addr);
+ void addToHashTable(LLMemoryChunk* chunk) ;
+ void removeFromHashTable(LLMemoryChunk* chunk) ;
+ void rehash() ;
+ bool fillHashTable(U16 start, U16 end, LLMemoryChunk* chunk) ;
+ LLMemoryChunk* findChunk(const char* addr) ;
+
+ void destroyPool() ;
+
+public:
+ enum
+ {
+ SMALL_ALLOCATION = 0, //from 8 bytes to 2KB(exclusive), page size 2KB, max chunk size is 4MB.
+ MEDIUM_ALLOCATION, //from 2KB to 512KB(exclusive), page size 32KB, max chunk size 4MB
+ LARGE_ALLOCATION, //from 512KB to 4MB(inclusive), page size 64KB, max chunk size 16MB
+ SUPER_ALLOCATION //allocation larger than 4MB.
+ };
+
+ enum
+ {
+ STATIC = 0 , //static pool(each alllocation stays for a long time) without threading support
+ VOLATILE, //Volatile pool(each allocation stays for a very short time) without threading support
+ STATIC_THREADED, //static pool with threading support
+ VOLATILE_THREADED, //volatile pool with threading support
+ MAX_TYPES
+ }; //pool types
+
+private:
+ LLMutex* mMutexp ;
+ U32 mMaxPoolSize;
+ U32 mReservedPoolSize ;
+
+ LLMemoryChunk* mChunkList[SUPER_ALLOCATION] ; //all memory chunks reserved by this pool, sorted by address
+ U16 mNumOfChunks ;
+ U16 mHashFactor ;
+
+ S32 mType ;
+
+ class LLChunkHashElement
+ {
+ public:
+ LLChunkHashElement() {mFirst = NULL ; mSecond = NULL ;}
+
+ bool add(LLMemoryChunk* chunk) ;
+ void remove(LLMemoryChunk* chunk) ;
+ LLMemoryChunk* findChunk(const char* addr) ;
+
+ bool empty() {return !mFirst && !mSecond; }
+ bool full() {return mFirst && mSecond; }
+ bool hasElement(LLMemoryChunk* chunk) {return mFirst == chunk || mSecond == chunk;}
+
+ private:
+ LLMemoryChunk* mFirst ;
+ LLMemoryChunk* mSecond ;
+ };
+ std::vector<LLChunkHashElement> mChunkHashList ;
+};
+
+class LL_COMMON_API LLPrivateMemoryPoolManager
+{
+private:
+ LLPrivateMemoryPoolManager(BOOL enabled, U32 max_pool_size) ;
+ ~LLPrivateMemoryPoolManager() ;
+
+public:
+ static LLPrivateMemoryPoolManager* getInstance() ;
+ static void initClass(BOOL enabled, U32 pool_size) ;
+ static void destroyClass() ;
+
+ LLPrivateMemoryPool* newPool(S32 type) ;
+ void deletePool(LLPrivateMemoryPool* pool) ;
+
+private:
+ std::vector<LLPrivateMemoryPool*> mPoolList ;
+ U32 mMaxPrivatePoolSize;
+
+ static LLPrivateMemoryPoolManager* sInstance ;
+ static BOOL sPrivatePoolEnabled;
+ static std::vector<LLPrivateMemoryPool*> sDanglingPoolList ;
+public:
+ //debug and statistics info.
+ void updateStatistics() ;
+
+ U32 mTotalReservedSize ;
+ U32 mTotalAllocatedSize ;
+
+public:
+#if __DEBUG_PRIVATE_MEM__
+ static char* allocate(LLPrivateMemoryPool* poolp, U32 size, const char* function, const int line) ;
+
+ typedef std::map<char*, std::string> mem_allocation_info_t ;
+ static mem_allocation_info_t sMemAllocationTracker;
+#else
+ static char* allocate(LLPrivateMemoryPool* poolp, U32 size) ;
+#endif
+ static void freeMem(LLPrivateMemoryPool* poolp, void* addr) ;
};
+//-------------------------------------------------------------------------------------
+#if __DEBUG_PRIVATE_MEM__
+#define ALLOCATE_MEM(poolp, size) LLPrivateMemoryPoolManager::allocate((poolp), (size), __FUNCTION__, __LINE__)
+#else
+#define ALLOCATE_MEM(poolp, size) LLPrivateMemoryPoolManager::allocate((poolp), (size))
+#endif
+#define FREE_MEM(poolp, addr) LLPrivateMemoryPoolManager::freeMem((poolp), (addr))
+//-------------------------------------------------------------------------------------
+
+//
+//the below singleton is used to test the private memory pool.
+//
+#if 0
+class LL_COMMON_API LLPrivateMemoryPoolTester
+{
+private:
+ LLPrivateMemoryPoolTester() ;
+ ~LLPrivateMemoryPoolTester() ;
+
+public:
+ static LLPrivateMemoryPoolTester* getInstance() ;
+ static void destroy() ;
+
+ void run(S32 type) ;
+
+private:
+ void correctnessTest() ;
+ void performanceTest() ;
+ void fragmentationtest() ;
+
+ void test(U32 min_size, U32 max_size, U32 stride, U32 times, bool random_deletion, bool output_statistics) ;
+ void testAndTime(U32 size, U32 times) ;
+
+#if 0
+public:
+ void* operator new(size_t size)
+ {
+ return (void*)sPool->allocate(size) ;
+ }
+ void operator delete(void* addr)
+ {
+ sPool->freeMem(addr) ;
+ }
+ void* operator new[](size_t size)
+ {
+ return (void*)sPool->allocate(size) ;
+ }
+ void operator delete[](void* addr)
+ {
+ sPool->freeMem(addr) ;
+ }
+#endif
+
+private:
+ static LLPrivateMemoryPoolTester* sInstance;
+ static LLPrivateMemoryPool* sPool ;
+ static LLPrivateMemoryPool* sThreadedPool ;
+};
+#if 0
+//static
+void* LLPrivateMemoryPoolTester::operator new(size_t size)
+{
+ return (void*)sPool->allocate(size) ;
+}
+
+//static
+void LLPrivateMemoryPoolTester::operator delete(void* addr)
+{
+ sPool->free(addr) ;
+}
+
+//static
+void* LLPrivateMemoryPoolTester::operator new[](size_t size)
+{
+ return (void*)sPool->allocate(size) ;
+}
+
+//static
+void LLPrivateMemoryPoolTester::operator delete[](void* addr)
+{
+ sPool->free(addr) ;
+}
+#endif
+#endif
// LLRefCount moved to llrefcount.h
// LLPointer moved to llpointer.h
@@ -62,4 +693,7 @@ private:
// LLSingleton moved to llsingleton.h
+
+
+
#endif
diff --git a/indra/llcommon/llmemorystream.cpp b/indra/llcommon/llmemorystream.cpp
index 5c01c955ae..707ac8fd0f 100644..100755
--- a/indra/llcommon/llmemorystream.cpp
+++ b/indra/llcommon/llmemorystream.cpp
@@ -4,31 +4,25 @@
* @date 2005-06-03
* @brief Buffer and stream for a fixed linear memory segment.
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -51,7 +45,7 @@ void LLMemoryStreamBuf::reset(const U8* start, S32 length)
int LLMemoryStreamBuf::underflow()
{
- //lldebugs << "LLMemoryStreamBuf::underflow()" << llendl;
+ //LL_DEBUGS() << "LLMemoryStreamBuf::underflow()" << LL_ENDL;
if(gptr() < egptr())
{
return *gptr();
diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h
index fa0f5d22f2..e28f012192 100644..100755
--- a/indra/llcommon/llmemorystream.h
+++ b/indra/llcommon/llmemorystream.h
@@ -4,31 +4,25 @@
* @date 2005-06-03
* @brief Implementation of a simple fixed memory stream
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llmemtype.cpp b/indra/llcommon/llmemtype.cpp
deleted file mode 100644
index 4e33439711..0000000000
--- a/indra/llcommon/llmemtype.cpp
+++ /dev/null
@@ -1,237 +0,0 @@
-/**
- * @file llmemtype.cpp
- * @brief Simple memory allocation/deallocation tracking stuff here
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "llmemtype.h"
-#include "llallocator.h"
-
-std::vector<char const *> LLMemType::DeclareMemType::mNameList;
-
-LLMemType::DeclareMemType LLMemType::MTYPE_INIT("Init");
-LLMemType::DeclareMemType LLMemType::MTYPE_STARTUP("Startup");
-LLMemType::DeclareMemType LLMemType::MTYPE_MAIN("Main");
-LLMemType::DeclareMemType LLMemType::MTYPE_FRAME("Frame");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_GATHER_INPUT("GatherInput");
-LLMemType::DeclareMemType LLMemType::MTYPE_JOY_KEY("JoyKey");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE("Idle");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_PUMP("IdlePump");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_NETWORK("IdleNetwork");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_REGIONS("IdleUpdateRegions");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_VIEWER_REGION("IdleUpdateViewerRegion");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_SURFACE("IdleUpdateSurface");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_UPDATE_PARCEL_OVERLAY("IdleUpdateParcelOverlay");
-LLMemType::DeclareMemType LLMemType::MTYPE_IDLE_AUDIO("IdleAudio");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING("CacheProcessPending");
-LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_ASKS("CacheProcessPendingAsks");
-LLMemType::DeclareMemType LLMemType::MTYPE_CACHE_PROCESS_PENDING_REPLIES("CacheProcessPendingReplies");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_CHECK_ALL("MessageCheckAll");
-LLMemType::DeclareMemType LLMemType::MTYPE_MESSAGE_PROCESS_ACKS("MessageProcessAcks");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_RENDER("Render");
-LLMemType::DeclareMemType LLMemType::MTYPE_SLEEP("Sleep");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_NETWORK("Network");
-LLMemType::DeclareMemType LLMemType::MTYPE_PHYSICS("Physics");
-LLMemType::DeclareMemType LLMemType::MTYPE_INTERESTLIST("InterestList");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEBASE("ImageBase");
-LLMemType::DeclareMemType LLMemType::MTYPE_IMAGERAW("ImageRaw");
-LLMemType::DeclareMemType LLMemType::MTYPE_IMAGEFORMATTED("ImageFormatted");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_APPFMTIMAGE("AppFmtImage");
-LLMemType::DeclareMemType LLMemType::MTYPE_APPRAWIMAGE("AppRawImage");
-LLMemType::DeclareMemType LLMemType::MTYPE_APPAUXRAWIMAGE("AppAuxRawImage");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_DRAWABLE("Drawable");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT("Object");
-LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE("ObjectProcessUpdate");
-LLMemType::DeclareMemType LLMemType::MTYPE_OBJECT_PROCESS_UPDATE_CORE("ObjectProcessUpdateCore");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY("Display");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE("DisplayUpdate");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_CAMERA("DisplayUpdateCam");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_GEOM("DisplayUpdateGeom");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SWAP("DisplaySwap");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_UPDATE_HUD("DisplayUpdateHud");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_GEN_REFLECTION("DisplayGenRefl");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE("DisplayImageUpdate");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_STATE_SORT("DisplayStateSort");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_SKY("DisplaySky");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_GEOM("DisplayRenderGeom");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_FLUSH("DisplayRenderFlush");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_UI("DisplayRenderUI");
-LLMemType::DeclareMemType LLMemType::MTYPE_DISPLAY_RENDER_ATTACHMENTS("DisplayRenderAttach");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DATA("VertexData");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CONSTRUCTOR("VertexConstr");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTRUCTOR("VertexDestr");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_VERTICES("VertexCreateVerts");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CREATE_INDICES("VertexCreateIndices");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_BUFFER("VertexDestroyBuff");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_DESTROY_INDICES("VertexDestroyIndices");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_VERTS("VertexUpdateVerts");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UPDATE_INDICES("VertexUpdateIndices");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_ALLOCATE_BUFFER("VertexAllocateBuffer");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_RESIZE_BUFFER("VertexResizeBuffer");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER("VertexMapBuffer");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_VERTICES("VertexMapBufferVerts");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_MAP_BUFFER_INDICES("VertexMapBufferIndices");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_UNMAP_BUFFER("VertexUnmapBuffer");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_STRIDE("VertexSetStride");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SET_BUFFER("VertexSetBuffer");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_SETUP_VERTEX_BUFFER("VertexSetupVertBuff");
-LLMemType::DeclareMemType LLMemType::MTYPE_VERTEX_CLEANUP_CLASS("VertexCleanupClass");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_SPACE_PARTITION("SpacePartition");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE("Pipeline");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_INIT("PipelineInit");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_BUFFERS("PipelineCreateBuffs");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RESTORE_GL("PipelineRestroGL");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UNLOAD_SHADERS("PipelineUnloadShaders");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_LIGHTING_DETAIL("PipelineLightingDetail");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GET_POOL_TYPE("PipelineGetPoolType");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_POOL("PipelineAddPool");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ALLOCATE_DRAWABLE("PipelineAllocDrawable");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_ADD_OBJECT("PipelineAddObj");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS("PipelineCreateObjs");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_MOVE("PipelineUpdateMove");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_GEOM("PipelineUpdateGeom");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_VISIBLE("PipelineMarkVisible");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_MOVED("PipelineMarkMoved");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_SHIFT("PipelineMarkShift");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_SHIFT_OBJECTS("PipelineShiftObjs");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_TEXTURED("PipelineMarkTextured");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_MARK_REBUILD("PipelineMarkRebuild");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_UPDATE_CULL("PipelineUpdateCull");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_STATE_SORT("PipelineStateSort");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_POST_SORT("PipelinePostSort");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS("PipelineHudEls");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_HL("PipelineRenderHL");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM("PipelineRenderGeom");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED("PipelineRenderGeomDef");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF("PipelineRenderGeomPostDef");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_GEOM_SHADOW("PipelineRenderGeomShadow");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_SELECT("PipelineRenderSelect");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_REBUILD_POOLS("PipelineRebuildPools");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_QUICK_LOOKUP("PipelineQuickLookup");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_OBJECTS("PipelineRenderObjs");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_GENERATE_IMPOSTOR("PipelineGenImpostors");
-LLMemType::DeclareMemType LLMemType::MTYPE_PIPELINE_RENDER_BLOOM("PipelineRenderBloom");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_UPKEEP_POOLS("UpkeepPools");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR("Avatar");
-LLMemType::DeclareMemType LLMemType::MTYPE_AVATAR_MESH("AvatarMesh");
-LLMemType::DeclareMemType LLMemType::MTYPE_PARTICLES("Particles");
-LLMemType::DeclareMemType LLMemType::MTYPE_REGIONS("Regions");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY("Inventory");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DRAW("InventoryDraw");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_BUILD_NEW_VIEWS("InventoryBuildNewViews");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_DO_FOLDER("InventoryDoFolder");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_POST_BUILD("InventoryPostBuild");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_FROM_XML("InventoryFromXML");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_CREATE_NEW_ITEM("InventoryCreateNewItem");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_INIT("InventoryViewInit");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_SHOW("InventoryViewShow");
-LLMemType::DeclareMemType LLMemType::MTYPE_INVENTORY_VIEW_TOGGLE("InventoryViewToggle");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_ANIMATION("Animation");
-LLMemType::DeclareMemType LLMemType::MTYPE_VOLUME("Volume");
-LLMemType::DeclareMemType LLMemType::MTYPE_PRIMITIVE("Primitive");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT("Script");
-LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_RUN("ScriptRun");
-LLMemType::DeclareMemType LLMemType::MTYPE_SCRIPT_BYTECODE("ScriptByteCode");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_PUMP("IoPump");
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_TCP("IoTCP");
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_BUFFER("IoBuffer");
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_HTTP_SERVER("IoHttpServer");
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_SERVER("IoSDServer");
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_SD_CLIENT("IoSDClient");
-LLMemType::DeclareMemType LLMemType::MTYPE_IO_URL_REQUEST("IOUrlRequest");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_DIRECTX_INIT("DirectXInit");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP1("Temp1");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP2("Temp2");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP3("Temp3");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP4("Temp4");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP5("Temp5");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP6("Temp6");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP7("Temp7");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP8("Temp8");
-LLMemType::DeclareMemType LLMemType::MTYPE_TEMP9("Temp9");
-
-LLMemType::DeclareMemType LLMemType::MTYPE_OTHER("Other");
-
-
-LLMemType::DeclareMemType::DeclareMemType(char const * st)
-{
- mID = (S32)mNameList.size();
- mName = st;
-
- mNameList.push_back(mName);
-}
-
-LLMemType::DeclareMemType::~DeclareMemType()
-{
-}
-
-LLMemType::LLMemType(LLMemType::DeclareMemType& dt)
-{
- mTypeIndex = dt.mID;
- LLAllocator::pushMemType(dt.mID);
-}
-
-LLMemType::~LLMemType()
-{
- LLAllocator::popMemType();
-}
-
-char const * LLMemType::getNameFromID(S32 id)
-{
- if (id < 0 || id >= (S32)DeclareMemType::mNameList.size())
- {
- return "INVALID";
- }
-
- return DeclareMemType::mNameList[id];
-}
-
diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h
deleted file mode 100644
index 677fad3034..0000000000
--- a/indra/llcommon/llmemtype.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/**
- * @file llmemtype.h
- * @brief Runtime memory usage debugging utilities.
- *
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_MEMTYPE_H
-#define LL_MEMTYPE_H
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
-//----------------------------------------------------------------------------
-
-#include "linden_common.h"
-//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-// WARNING: Never commit with MEM_TRACK_MEM == 1
-//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-#define MEM_TRACK_MEM (0 && LL_WINDOWS)
-
-#include <vector>
-
-#define MEM_TYPE_NEW(T)
-
-class LL_COMMON_API LLMemType
-{
-public:
-
- // class we'll initialize all instances of as
- // static members of MemType. Then use
- // to construct any new mem type.
- class LL_COMMON_API DeclareMemType
- {
- public:
- DeclareMemType(char const * st);
- ~DeclareMemType();
-
- S32 mID;
- char const * mName;
-
- // array so we can map an index ID to Name
- static std::vector<char const *> mNameList;
- };
-
- LLMemType(DeclareMemType& dt);
- ~LLMemType();
-
- static char const * getNameFromID(S32 id);
-
- static DeclareMemType MTYPE_INIT;
- static DeclareMemType MTYPE_STARTUP;
- static DeclareMemType MTYPE_MAIN;
- static DeclareMemType MTYPE_FRAME;
-
- static DeclareMemType MTYPE_GATHER_INPUT;
- static DeclareMemType MTYPE_JOY_KEY;
-
- static DeclareMemType MTYPE_IDLE;
- static DeclareMemType MTYPE_IDLE_PUMP;
- static DeclareMemType MTYPE_IDLE_NETWORK;
- static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
- static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
- static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
- static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
- static DeclareMemType MTYPE_IDLE_AUDIO;
-
- static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
- static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
- static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
-
- static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
- static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
-
- static DeclareMemType MTYPE_RENDER;
- static DeclareMemType MTYPE_SLEEP;
-
- static DeclareMemType MTYPE_NETWORK;
- static DeclareMemType MTYPE_PHYSICS;
- static DeclareMemType MTYPE_INTERESTLIST;
-
- static DeclareMemType MTYPE_IMAGEBASE;
- static DeclareMemType MTYPE_IMAGERAW;
- static DeclareMemType MTYPE_IMAGEFORMATTED;
-
- static DeclareMemType MTYPE_APPFMTIMAGE;
- static DeclareMemType MTYPE_APPRAWIMAGE;
- static DeclareMemType MTYPE_APPAUXRAWIMAGE;
-
- static DeclareMemType MTYPE_DRAWABLE;
-
- static DeclareMemType MTYPE_OBJECT;
- static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
- static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
-
- static DeclareMemType MTYPE_DISPLAY;
- static DeclareMemType MTYPE_DISPLAY_UPDATE;
- static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
- static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
- static DeclareMemType MTYPE_DISPLAY_SWAP;
- static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
- static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
- static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
- static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
- static DeclareMemType MTYPE_DISPLAY_SKY;
- static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
- static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
- static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
- static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
-
- static DeclareMemType MTYPE_VERTEX_DATA;
- static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
- static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
- static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
- static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
- static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
- static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
- static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
- static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
- static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
- static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
- static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
- static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
- static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
- static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
- static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
- static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
- static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
- static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
-
- static DeclareMemType MTYPE_SPACE_PARTITION;
-
- static DeclareMemType MTYPE_PIPELINE;
- static DeclareMemType MTYPE_PIPELINE_INIT;
- static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
- static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
- static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
- static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
- static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
- static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
- static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
- static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
- static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
- static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
- static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
- static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
- static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
- static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
- static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
- static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
- static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
- static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
- static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
- static DeclareMemType MTYPE_PIPELINE_POST_SORT;
-
- static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
- static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
- static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
- static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
- static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
- static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
- static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
- static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
-
- static DeclareMemType MTYPE_UPKEEP_POOLS;
-
- static DeclareMemType MTYPE_AVATAR;
- static DeclareMemType MTYPE_AVATAR_MESH;
- static DeclareMemType MTYPE_PARTICLES;
- static DeclareMemType MTYPE_REGIONS;
-
- static DeclareMemType MTYPE_INVENTORY;
- static DeclareMemType MTYPE_INVENTORY_DRAW;
- static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
- static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
- static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
- static DeclareMemType MTYPE_INVENTORY_FROM_XML;
- static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
- static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
- static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
- static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
-
- static DeclareMemType MTYPE_ANIMATION;
- static DeclareMemType MTYPE_VOLUME;
- static DeclareMemType MTYPE_PRIMITIVE;
-
- static DeclareMemType MTYPE_SCRIPT;
- static DeclareMemType MTYPE_SCRIPT_RUN;
- static DeclareMemType MTYPE_SCRIPT_BYTECODE;
-
- static DeclareMemType MTYPE_IO_PUMP;
- static DeclareMemType MTYPE_IO_TCP;
- static DeclareMemType MTYPE_IO_BUFFER;
- static DeclareMemType MTYPE_IO_HTTP_SERVER;
- static DeclareMemType MTYPE_IO_SD_SERVER;
- static DeclareMemType MTYPE_IO_SD_CLIENT;
- static DeclareMemType MTYPE_IO_URL_REQUEST;
-
- static DeclareMemType MTYPE_DIRECTX_INIT;
-
- static DeclareMemType MTYPE_TEMP1;
- static DeclareMemType MTYPE_TEMP2;
- static DeclareMemType MTYPE_TEMP3;
- static DeclareMemType MTYPE_TEMP4;
- static DeclareMemType MTYPE_TEMP5;
- static DeclareMemType MTYPE_TEMP6;
- static DeclareMemType MTYPE_TEMP7;
- static DeclareMemType MTYPE_TEMP8;
- static DeclareMemType MTYPE_TEMP9;
-
- static DeclareMemType MTYPE_OTHER; // Special; used by display code
-
- S32 mTypeIndex;
-};
-
-//----------------------------------------------------------------------------
-
-#endif
-
diff --git a/indra/llcommon/llmetricperformancetester.cpp b/indra/llcommon/llmetricperformancetester.cpp
new file mode 100755
index 0000000000..1fc821d9a9
--- /dev/null
+++ b/indra/llcommon/llmetricperformancetester.cpp
@@ -0,0 +1,335 @@
+/**
+ * @file llmetricperformancetester.cpp
+ * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "indra_constants.h"
+#include "llerror.h"
+#include "llsdserialize.h"
+#include "lltreeiterators.h"
+#include "llmetricperformancetester.h"
+#include "llfasttimer.h"
+
+//----------------------------------------------------------------------------------------------
+// LLMetricPerformanceTesterBasic : static methods and testers management
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterBasic::name_tester_map_t LLMetricPerformanceTesterBasic::sTesterMap ;
+
+/*static*/
+void LLMetricPerformanceTesterBasic::cleanClass()
+{
+ for (name_tester_map_t::iterator iter = sTesterMap.begin() ; iter != sTesterMap.end() ; ++iter)
+ {
+ delete iter->second ;
+ }
+ sTesterMap.clear() ;
+}
+
+/*static*/
+BOOL LLMetricPerformanceTesterBasic::addTester(LLMetricPerformanceTesterBasic* tester)
+{
+ llassert_always(tester != NULL);
+ std::string name = tester->getTesterName() ;
+ if (getTester(name))
+ {
+ LL_ERRS() << "Tester name is already used by some other tester : " << name << LL_ENDL ;
+ return FALSE;
+ }
+
+ sTesterMap.insert(std::make_pair(name, tester));
+ return TRUE;
+}
+
+/*static*/
+void LLMetricPerformanceTesterBasic::deleteTester(std::string name)
+{
+ name_tester_map_t::iterator tester = sTesterMap.find(name);
+ if (tester != sTesterMap.end())
+ {
+ delete tester->second;
+ sTesterMap.erase(tester);
+ }
+}
+
+/*static*/
+LLMetricPerformanceTesterBasic* LLMetricPerformanceTesterBasic::getTester(std::string name)
+{
+ // Check for the requested metric name
+ name_tester_map_t::iterator found_it = sTesterMap.find(name) ;
+ if (found_it != sTesterMap.end())
+ {
+ return found_it->second ;
+ }
+ return NULL ;
+}
+
+/*static*/
+// Return TRUE if this metric is requested or if the general default "catch all" metric is requested
+BOOL LLMetricPerformanceTesterBasic::isMetricLogRequested(std::string name)
+{
+ return (LLTrace::BlockTimer::sMetricLog && ((LLTrace::BlockTimer::sLogName == name) || (LLTrace::BlockTimer::sLogName == DEFAULT_METRIC_NAME)));
+}
+
+/*static*/
+LLSD LLMetricPerformanceTesterBasic::analyzeMetricPerformanceLog(std::istream& is)
+{
+ LLSD ret;
+ LLSD cur;
+
+ while (!is.eof() && LLSDParser::PARSE_FAILURE != LLSDSerialize::fromXML(cur, is))
+ {
+ for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
+ {
+ std::string label = iter->first;
+
+ LLMetricPerformanceTesterBasic* tester = LLMetricPerformanceTesterBasic::getTester(iter->second["Name"].asString()) ;
+ if(tester)
+ {
+ ret[label]["Name"] = iter->second["Name"] ;
+
+ S32 num_of_metrics = tester->getNumberOfMetrics() ;
+ for(S32 index = 0 ; index < num_of_metrics ; index++)
+ {
+ ret[label][ tester->getMetricName(index) ] = iter->second[ tester->getMetricName(index) ] ;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*static*/
+void LLMetricPerformanceTesterBasic::doAnalysisMetrics(std::string baseline, std::string target, std::string output)
+{
+ if(!LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters())
+ {
+ return ;
+ }
+
+ // Open baseline and current target, exit if one is inexistent
+ std::ifstream base_is(baseline.c_str());
+ std::ifstream target_is(target.c_str());
+ if (!base_is.is_open() || !target_is.is_open())
+ {
+ LL_WARNS() << "'-analyzeperformance' error : baseline or current target file inexistent" << LL_ENDL;
+ base_is.close();
+ target_is.close();
+ return;
+ }
+
+ //analyze baseline
+ LLSD base = analyzeMetricPerformanceLog(base_is);
+ base_is.close();
+
+ //analyze current
+ LLSD current = analyzeMetricPerformanceLog(target_is);
+ target_is.close();
+
+ //output comparision
+ std::ofstream os(output.c_str());
+
+ os << "Label, Metric, Base(B), Target(T), Diff(T-B), Percentage(100*T/B)\n";
+ for(LLMetricPerformanceTesterBasic::name_tester_map_t::iterator iter = LLMetricPerformanceTesterBasic::sTesterMap.begin() ;
+ iter != LLMetricPerformanceTesterBasic::sTesterMap.end() ; ++iter)
+ {
+ LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)iter->second) ;
+ tester->analyzePerformance(&os, &base, &current) ;
+ }
+
+ os.flush();
+ os.close();
+}
+
+
+//----------------------------------------------------------------------------------------------
+// LLMetricPerformanceTesterBasic : Tester instance methods
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterBasic::LLMetricPerformanceTesterBasic(std::string name) :
+ mName(name),
+ mCount(0)
+{
+ if (mName == std::string())
+ {
+ LL_ERRS() << "LLMetricPerformanceTesterBasic construction invalid : Empty name passed to constructor" << LL_ENDL ;
+ }
+
+ mValidInstance = LLMetricPerformanceTesterBasic::addTester(this) ;
+}
+
+LLMetricPerformanceTesterBasic::~LLMetricPerformanceTesterBasic()
+{
+}
+
+void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd)
+{
+ incrementCurrentCount() ;
+ (*sd)[getCurrentLabelName()]["Name"] = mName ;
+}
+
+void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd)
+{
+ LLTrace::BlockTimer::pushLog(*sd);
+}
+
+void LLMetricPerformanceTesterBasic::outputTestResults()
+{
+ LLSD sd;
+
+ preOutputTestResults(&sd) ;
+ outputTestRecord(&sd) ;
+ postOutputTestResults(&sd) ;
+}
+
+void LLMetricPerformanceTesterBasic::addMetric(std::string str)
+{
+ mMetricStrings.push_back(str) ;
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterBasic::analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current)
+{
+ resetCurrentCount() ;
+
+ std::string current_label = getCurrentLabelName();
+ BOOL in_base = (*base).has(current_label) ;
+ BOOL in_current = (*current).has(current_label) ;
+
+ while(in_base || in_current)
+ {
+ LLSD::String label = current_label ;
+
+ if(in_base && in_current)
+ {
+ *os << llformat("%s\n", label.c_str()) ;
+
+ for(U32 index = 0 ; index < mMetricStrings.size() ; index++)
+ {
+ switch((*current)[label][ mMetricStrings[index] ].type())
+ {
+ case LLSD::TypeInteger:
+ compareTestResults(os, mMetricStrings[index],
+ (S32)((*base)[label][ mMetricStrings[index] ].asInteger()), (S32)((*current)[label][ mMetricStrings[index] ].asInteger())) ;
+ break ;
+ case LLSD::TypeReal:
+ compareTestResults(os, mMetricStrings[index],
+ (F32)((*base)[label][ mMetricStrings[index] ].asReal()), (F32)((*current)[label][ mMetricStrings[index] ].asReal())) ;
+ break;
+ default:
+ LL_ERRS() << "unsupported metric " << mMetricStrings[index] << " LLSD type: " << (S32)(*current)[label][ mMetricStrings[index] ].type() << LL_ENDL ;
+ }
+ }
+ }
+
+ incrementCurrentCount();
+ current_label = getCurrentLabelName();
+ in_base = (*base).has(current_label) ;
+ in_current = (*current).has(current_label) ;
+ }
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterBasic::compareTestResults(std::ofstream* os, std::string metric_string, S32 v_base, S32 v_current)
+{
+ *os << llformat(" ,%s, %d, %d, %d, %.4f\n", metric_string.c_str(), v_base, v_current,
+ v_current - v_base, (v_base != 0) ? 100.f * v_current / v_base : 0) ;
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterBasic::compareTestResults(std::ofstream* os, std::string metric_string, F32 v_base, F32 v_current)
+{
+ *os << llformat(" ,%s, %.4f, %.4f, %.4f, %.4f\n", metric_string.c_str(), v_base, v_current,
+ v_current - v_base, (fabs(v_base) > 0.0001f) ? 100.f * v_current / v_base : 0.f ) ;
+}
+
+//----------------------------------------------------------------------------------------------
+// LLMetricPerformanceTesterWithSession
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterWithSession::LLMetricPerformanceTesterWithSession(std::string name) :
+ LLMetricPerformanceTesterBasic(name),
+ mBaseSessionp(NULL),
+ mCurrentSessionp(NULL)
+{
+}
+
+LLMetricPerformanceTesterWithSession::~LLMetricPerformanceTesterWithSession()
+{
+ if (mBaseSessionp)
+ {
+ delete mBaseSessionp ;
+ mBaseSessionp = NULL ;
+ }
+ if (mCurrentSessionp)
+ {
+ delete mCurrentSessionp ;
+ mCurrentSessionp = NULL ;
+ }
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterWithSession::analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current)
+{
+ // Load the base session
+ resetCurrentCount() ;
+ mBaseSessionp = loadTestSession(base) ;
+
+ // Load the current session
+ resetCurrentCount() ;
+ mCurrentSessionp = loadTestSession(current) ;
+
+ if (!mBaseSessionp || !mCurrentSessionp)
+ {
+ LL_ERRS() << "Error loading test sessions." << LL_ENDL ;
+ }
+
+ // Compare
+ compareTestSessions(os) ;
+
+ // Release memory
+ if (mBaseSessionp)
+ {
+ delete mBaseSessionp ;
+ mBaseSessionp = NULL ;
+ }
+ if (mCurrentSessionp)
+ {
+ delete mCurrentSessionp ;
+ mCurrentSessionp = NULL ;
+ }
+}
+
+
+//----------------------------------------------------------------------------------------------
+// LLTestSession
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterWithSession::LLTestSession::~LLTestSession()
+{
+}
+
diff --git a/indra/llcommon/llmetricperformancetester.h b/indra/llcommon/llmetricperformancetester.h
new file mode 100755
index 0000000000..1a18cdf36f
--- /dev/null
+++ b/indra/llcommon/llmetricperformancetester.h
@@ -0,0 +1,215 @@
+/**
+ * @file llmetricperformancetester.h
+ * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_METRICPERFORMANCETESTER_H
+#define LL_METRICPERFORMANCETESTER_H
+
+char const* const DEFAULT_METRIC_NAME = "metric";
+
+/**
+ * @class LLMetricPerformanceTesterBasic
+ * @brief Performance Metric Base Class
+ */
+class LL_COMMON_API LLMetricPerformanceTesterBasic
+{
+public:
+ /**
+ * @brief Creates a basic tester instance.
+ * @param[in] name - Unique string identifying this tester instance.
+ */
+ LLMetricPerformanceTesterBasic(std::string name);
+ virtual ~LLMetricPerformanceTesterBasic();
+
+ /**
+ * @return Returns true if the instance has been added to the tester map.
+ * Need to be tested after creation of a tester instance so to know if the tester is correctly handled.
+ * A tester might not be added to the map if another tester with the same name already exists.
+ */
+ BOOL isValid() const { return mValidInstance; }
+
+ /**
+ * @brief Write a set of test results to the log LLSD.
+ */
+ void outputTestResults() ;
+
+ /**
+ * @brief Compare the test results.
+ * By default, compares the test results against the baseline one by one, item by item,
+ * in the increasing order of the LLSD record counter, starting from the first one.
+ */
+ virtual void analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) ;
+
+ static void doAnalysisMetrics(std::string baseline, std::string target, std::string output) ;
+
+ /**
+ * @return Returns the number of the test metrics in this tester instance.
+ */
+ S32 getNumberOfMetrics() const { return mMetricStrings.size() ;}
+ /**
+ * @return Returns the metric name at index
+ * @param[in] index - Index on the list of metrics managed by this tester instance.
+ */
+ std::string getMetricName(S32 index) const { return mMetricStrings[index] ;}
+
+protected:
+ /**
+ * @return Returns the name of this tester instance.
+ */
+ std::string getTesterName() const { return mName ;}
+
+ /**
+ * @brief Insert a new metric to be managed by this tester instance.
+ * @param[in] str - Unique string identifying the new metric.
+ */
+ void addMetric(std::string str) ;
+
+ /**
+ * @brief Compare test results, provided in 2 flavors: compare integers and compare floats.
+ * @param[out] os - Formatted output string holding the compared values.
+ * @param[in] metric_string - Name of the metric.
+ * @param[in] v_base - Base value of the metric.
+ * @param[in] v_current - Current value of the metric.
+ */
+ virtual void compareTestResults(std::ofstream* os, std::string metric_string, S32 v_base, S32 v_current) ;
+ virtual void compareTestResults(std::ofstream* os, std::string metric_string, F32 v_base, F32 v_current) ;
+
+ /**
+ * @brief Reset internal record count. Count starts with 1.
+ */
+ void resetCurrentCount() { mCount = 1; }
+ /**
+ * @brief Increment internal record count.
+ */
+ void incrementCurrentCount() { mCount++; }
+ /**
+ * @return Returns the label to be used for the current count. It's "TesterName"-"Count".
+ */
+ std::string getCurrentLabelName() const { return llformat("%s-%d", mName.c_str(), mCount) ;}
+
+ /**
+ * @brief Write a test record to the LLSD. Implementers need to overload this method.
+ * @param[out] sd - The LLSD record to store metric data into.
+ */
+ virtual void outputTestRecord(LLSD* sd) = 0 ;
+
+private:
+ void preOutputTestResults(LLSD* sd) ;
+ void postOutputTestResults(LLSD* sd) ;
+ static LLSD analyzeMetricPerformanceLog(std::istream& is) ;
+
+ std::string mName ; // Name of this tester instance
+ S32 mCount ; // Current record count
+ BOOL mValidInstance; // TRUE if the instance is managed by the map
+ std::vector< std::string > mMetricStrings ; // Metrics strings
+
+// Static members managing the collection of testers
+public:
+ // Map of all the tester instances in use
+ typedef std::map< std::string, LLMetricPerformanceTesterBasic* > name_tester_map_t;
+ static name_tester_map_t sTesterMap ;
+
+ /**
+ * @return Returns a pointer to the tester
+ * @param[in] name - Name of the tester instance queried.
+ */
+ static LLMetricPerformanceTesterBasic* getTester(std::string name) ;
+
+ /**
+ * @return Delete the named tester from the list
+ * @param[in] name - Name of the tester instance to delete.
+ */
+ static void deleteTester(std::string name);
+
+ /**
+ * @return Returns TRUE if that metric *or* the default catch all metric has been requested to be logged
+ * @param[in] name - Name of the tester queried.
+ */
+ static BOOL isMetricLogRequested(std::string name);
+
+ /**
+ * @return Returns TRUE if there's a tester defined, FALSE otherwise.
+ */
+ static BOOL hasMetricPerformanceTesters() { return !sTesterMap.empty() ;}
+ /**
+ * @brief Delete all testers and reset the tester map
+ */
+ static void cleanClass() ;
+
+private:
+ // Add a tester to the map. Returns false if adding fails.
+ static BOOL addTester(LLMetricPerformanceTesterBasic* tester) ;
+};
+
+/**
+ * @class LLMetricPerformanceTesterWithSession
+ * @brief Performance Metric Class with custom session
+ */
+class LL_COMMON_API LLMetricPerformanceTesterWithSession : public LLMetricPerformanceTesterBasic
+{
+public:
+ /**
+ * @param[in] name - Unique string identifying this tester instance.
+ */
+ LLMetricPerformanceTesterWithSession(std::string name);
+ virtual ~LLMetricPerformanceTesterWithSession();
+
+ /**
+ * @brief Compare the test results.
+ * This will be loading the base and current sessions and compare them using the virtual
+ * abstract methods loadTestSession() and compareTestSessions()
+ */
+ virtual void analyzePerformance(std::ofstream* os, LLSD* base, LLSD* current) ;
+
+protected:
+ /**
+ * @class LLMetricPerformanceTesterWithSession::LLTestSession
+ * @brief Defines an interface for the two abstract virtual functions loadTestSession() and compareTestSessions()
+ */
+ class LL_COMMON_API LLTestSession
+ {
+ public:
+ virtual ~LLTestSession() ;
+ };
+
+ /**
+ * @brief Convert an LLSD log into a test session.
+ * @param[in] log - The LLSD record
+ * @return Returns the record as a test session
+ */
+ virtual LLMetricPerformanceTesterWithSession::LLTestSession* loadTestSession(LLSD* log) = 0;
+
+ /**
+ * @brief Compare the base session and the target session. Assumes base and current sessions have been loaded.
+ * @param[out] os - The comparison result as a standard stream
+ */
+ virtual void compareTestSessions(std::ofstream* os) = 0;
+
+ LLTestSession* mBaseSessionp;
+ LLTestSession* mCurrentSessionp;
+};
+
+#endif
+
diff --git a/indra/llcommon/llmetrics.cpp b/indra/llcommon/llmetrics.cpp
index 30e5d435ae..d40afe5160 100644..100755
--- a/indra/llcommon/llmetrics.cpp
+++ b/indra/llcommon/llmetrics.cpp
@@ -4,31 +4,25 @@
* @date 2007-05-25
* @brief Metrics accumulation and associated functions
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -71,7 +65,7 @@ void LLMetricsImpl::recordEventDetails(const std::string& location,
metrics["location"] = location;
metrics["stats"] = stats;
- llinfos << "LLMETRICS: " << (LLSDNotationStreamer(metrics)) << llendl;
+ LL_INFOS() << "LLMETRICS: " << (LLSDNotationStreamer(metrics)) << LL_ENDL;
}
// Store this:
@@ -134,7 +128,7 @@ void LLMetricsImpl::printTotals(LLSD metadata)
out_sd["stats"] = stats;
- llinfos << "LLMETRICS: AGGREGATE: " << LLSDOStreamer<LLSDNotationFormatter>(out_sd) << llendl;
+ LL_INFOS() << "LLMETRICS: AGGREGATE: " << LLSDOStreamer<LLSDNotationFormatter>(out_sd) << LL_ENDL;
}
LLMetrics::LLMetrics()
diff --git a/indra/llcommon/llmetrics.h b/indra/llcommon/llmetrics.h
index f6f49eb456..85a6986049 100644..100755
--- a/indra/llcommon/llmetrics.h
+++ b/indra/llcommon/llmetrics.h
@@ -4,31 +4,25 @@
* @date 2007-05-25
* @brief Declaration of metrics accumulation and associated functions
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,7 +38,7 @@ public:
LLMetrics();
virtual ~LLMetrics();
- // Adds this event to aggregate totals and records details to syslog (llinfos)
+ // Adds this event to aggregate totals and records details to syslog (LL_INFOS())
virtual void recordEventDetails(const std::string& location,
const std::string& mesg,
bool success,
diff --git a/indra/llcommon/llmortician.cpp b/indra/llcommon/llmortician.cpp
index c800b654ba..287f096eae 100644..100755
--- a/indra/llcommon/llmortician.cpp
+++ b/indra/llcommon/llmortician.cpp
@@ -1,31 +1,25 @@
/**
* @file llmortician.cpp
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h
index 27bd8cd9b5..9517e2db5e 100644..100755
--- a/indra/llcommon/llmortician.h
+++ b/indra/llcommon/llmortician.h
@@ -2,31 +2,25 @@
* @file llmortician.h
* @brief Base class for delayed deletions.
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,6 +28,7 @@
#define LLMORTICIAN_H
#include "stdtypes.h"
+#include <list>
class LL_COMMON_API LLMortician
{
diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp
new file mode 100644
index 0000000000..9c13ef9e30
--- /dev/null
+++ b/indra/llcommon/llmutex.cpp
@@ -0,0 +1,205 @@
+/**
+ * @file llmutex.cpp
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+
+#include "apr_portable.h"
+
+#include "llmutex.h"
+#include "llthread.h"
+
+//============================================================================
+
+LLMutex::LLMutex(apr_pool_t *poolp) :
+ mAPRMutexp(NULL), mCount(0), mLockingThread(NO_THREAD)
+{
+ //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_UNNESTED, mAPRPoolp);
+}
+
+
+LLMutex::~LLMutex()
+{
+#if MUTEX_DEBUG
+ //bad assertion, the subclass LLSignal might be "locked", and that's OK
+ //llassert_always(!isLocked()); // better not be locked!
+#endif
+ if (ll_apr_is_initialized())
+ {
+ apr_thread_mutex_destroy(mAPRMutexp);
+ if (mIsLocalPool)
+ {
+ apr_pool_destroy(mAPRPoolp);
+ }
+ }
+ mAPRMutexp = NULL;
+}
+
+
+void LLMutex::lock()
+{
+ if(isSelfLocked())
+ { //redundant lock
+ mCount++;
+ return;
+ }
+
+ apr_thread_mutex_lock(mAPRMutexp);
+
+#if MUTEX_DEBUG
+ // Have to have the lock before we can access the debug info
+ U32 id = LLThread::currentID();
+ if (mIsLocked[id] != FALSE)
+ LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL;
+ mIsLocked[id] = TRUE;
+#endif
+
+ mLockingThread = LLThread::currentID();
+}
+
+void LLMutex::unlock()
+{
+ if (mCount > 0)
+ { //not the root unlock
+ mCount--;
+ return;
+ }
+
+#if MUTEX_DEBUG
+ // Access the debug info while we have the lock
+ U32 id = LLThread::currentID();
+ if (mIsLocked[id] != TRUE)
+ LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL;
+ mIsLocked[id] = FALSE;
+#endif
+
+ mLockingThread = NO_THREAD;
+ 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;
+ }
+}
+
+bool LLMutex::isSelfLocked()
+{
+ return mLockingThread == LLThread::currentID();
+}
+
+U32 LLMutex::lockingThread() const
+{
+ return mLockingThread;
+}
+
+bool LLMutex::trylock()
+{
+ if(isSelfLocked())
+ { //redundant lock
+ mCount++;
+ return true;
+ }
+
+ apr_status_t status(apr_thread_mutex_trylock(mAPRMutexp));
+ if (APR_STATUS_IS_EBUSY(status))
+ {
+ return false;
+ }
+
+#if MUTEX_DEBUG
+ // Have to have the lock before we can access the debug info
+ U32 id = LLThread::currentID();
+ if (mIsLocked[id] != FALSE)
+ LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL;
+ mIsLocked[id] = TRUE;
+#endif
+
+ mLockingThread = LLThread::currentID();
+ return true;
+}
+
+//============================================================================
+
+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()
+{
+ if (!isLocked())
+ { //mAPRMutexp MUST be locked before calling apr_thread_cond_wait
+ apr_thread_mutex_lock(mAPRMutexp);
+#if MUTEX_DEBUG
+ // avoid asserts on destruction in non-release builds
+ U32 id = LLThread::currentID();
+ mIsLocked[id] = TRUE;
+#endif
+ }
+ 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/llmutex.h b/indra/llcommon/llmutex.h
new file mode 100644
index 0000000000..3659a319a2
--- /dev/null
+++ b/indra/llcommon/llmutex.h
@@ -0,0 +1,141 @@
+/**
+ * @file llmutex.h
+ * @brief Base classes for mutex and condition handling.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMUTEX_H
+#define LL_LLMUTEX_H
+
+#include "stdtypes.h"
+
+//============================================================================
+
+#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
+
+struct apr_thread_mutex_t;
+struct apr_pool_t;
+struct apr_thread_cond_t;
+
+class LL_COMMON_API LLMutex
+{
+public:
+ typedef enum
+ {
+ NO_THREAD = 0xFFFFFFFF
+ } e_locking_thread;
+
+ LLMutex(apr_pool_t *apr_poolp = NULL); // NULL pool constructs a new pool for the mutex
+ virtual ~LLMutex();
+
+ void lock(); // blocks
+ bool trylock(); // non-blocking, returns true if lock held.
+ void unlock(); // undefined behavior when called on mutex not being held
+ bool isLocked(); // non-blocking, but does do a lock/unlock so not free
+ bool isSelfLocked(); //return true if locked in a same thread
+ U32 lockingThread() const; //get ID of locking thread
+
+protected:
+ apr_thread_mutex_t *mAPRMutexp;
+ mutable U32 mCount;
+ mutable U32 mLockingThread;
+
+ apr_pool_t *mAPRPoolp;
+ BOOL mIsLocalPool;
+
+#if MUTEX_DEBUG
+ std::map<U32, BOOL> mIsLocked;
+#endif
+};
+
+// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
+class LL_COMMON_API 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;
+
+ if(mMutex)
+ mMutex->lock();
+ }
+ ~LLMutexLock()
+ {
+ if(mMutex)
+ mMutex->unlock();
+ }
+private:
+ LLMutex* mMutex;
+};
+
+//============================================================================
+
+// Scoped locking class similar in function to LLMutexLock but uses
+// the trylock() method to conditionally acquire lock without
+// blocking. Caller resolves the resulting condition by calling
+// the isLocked() method and either punts or continues as indicated.
+//
+// Mostly of interest to callers needing to avoid stalls who can
+// guarantee another attempt at a later time.
+
+class LLMutexTrylock
+{
+public:
+ LLMutexTrylock(LLMutex* mutex)
+ : mMutex(mutex),
+ mLocked(false)
+ {
+ if (mMutex)
+ mLocked = mMutex->trylock();
+ }
+
+ ~LLMutexTrylock()
+ {
+ if (mMutex && mLocked)
+ mMutex->unlock();
+ }
+
+ bool isLocked() const
+ {
+ return mLocked;
+ }
+
+private:
+ LLMutex* mMutex;
+ bool mLocked;
+};
+#endif // LL_LLTHREAD_H
diff --git a/indra/llcommon/llnametable.h b/indra/llcommon/llnametable.h
index 335a191be3..d3283543f3 100644
--- a/indra/llcommon/llnametable.h
+++ b/indra/llcommon/llnametable.h
@@ -2,31 +2,25 @@
* @file llnametable.h
* @brief LLNameTable class is a table to associate pointers with string names
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lloptioninterface.cpp b/indra/llcommon/lloptioninterface.cpp
deleted file mode 100644
index 68c1ff1c41..0000000000
--- a/indra/llcommon/lloptioninterface.cpp
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * @file lloptioninterface.cpp
- * @brief
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "lloptioninterface.h"
-
-
-LLOptionInterface::~LLOptionInterface()
-{
-
-}
diff --git a/indra/llcommon/lloptioninterface.h b/indra/llcommon/lloptioninterface.h
deleted file mode 100644
index 4faf95f5e1..0000000000
--- a/indra/llcommon/lloptioninterface.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * @file lloptioninterface.h
- * @brief
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLOPTIONINTERFACE_H
-#define LL_LLOPTIONINTERFACE_H
-
-#include "linden_common.h"
-
-class LLSD;
-class LLOptionInterface
-{
-public:
- virtual ~LLOptionInterface() = 0;
- virtual LLSD getOption(const std::string& name) const = 0;
-};
-
-#endif
diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h
index e6c736a263..9a6453ea48 100644..100755
--- a/indra/llcommon/llpointer.h
+++ b/indra/llcommon/llpointer.h
@@ -2,37 +2,32 @@
* @file llpointer.h
* @brief A reference-counted pointer for objects derived from LLRefCount
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLPOINTER_H
#define LLPOINTER_H
#include "llerror.h" // *TODO: consider eliminating this
+#include "llmutex.h"
//----------------------------------------------------------------------------
// RefCount objects should generally only be accessed by way of LLPointer<>'s
@@ -103,6 +98,126 @@ public:
LLPointer<Type>& operator =(Type* ptr)
{
+ assign(ptr);
+ return *this;
+ }
+
+ LLPointer<Type>& operator =(const LLPointer<Type>& ptr)
+ {
+ assign(ptr);
+ 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)
+ {
+ assign(ptr.get());
+ return *this;
+ }
+
+ // Just exchange the pointers, which will not change the reference counts.
+ static void swap(LLPointer<Type>& a, LLPointer<Type>& b)
+ {
+ Type* temp = a.mPointer;
+ a.mPointer = b.mPointer;
+ b.mPointer = temp;
+ }
+
+protected:
+#ifdef LL_LIBRARY_INCLUDE
+ void ref();
+ void unref();
+#else
+
+ void assign(const LLPointer<Type>& ptr)
+ {
+ if( mPointer != ptr.mPointer )
+ {
+ unref();
+ mPointer = ptr.mPointer;
+ ref();
+ }
+ }
+ void ref()
+ {
+ if (mPointer)
+ {
+ mPointer->ref();
+ }
+ }
+
+ void unref()
+ {
+ if (mPointer)
+ {
+ Type *temp = mPointer;
+ mPointer = NULL;
+ temp->unref();
+ if (mPointer != NULL)
+ {
+ LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL;
+ unref();
+ }
+ }
+ }
+#endif
+protected:
+ Type* mPointer;
+};
+
+template <class Type> class LLConstPointer
+{
+public:
+ LLConstPointer() :
+ mPointer(NULL)
+ {
+ }
+
+ LLConstPointer(const Type* ptr) :
+ mPointer(ptr)
+ {
+ ref();
+ }
+
+ LLConstPointer(const LLConstPointer<Type>& ptr) :
+ mPointer(ptr.mPointer)
+ {
+ ref();
+ }
+
+ // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
+ template<typename Subclass>
+ LLConstPointer(const LLConstPointer<Subclass>& ptr) :
+ mPointer(ptr.get())
+ {
+ ref();
+ }
+
+ ~LLConstPointer()
+ {
+ unref();
+ }
+
+ const Type* get() const { return mPointer; }
+ const Type* operator->() const { return mPointer; }
+ const Type& operator*() const { 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 const Type*() const { return mPointer; }
+ bool operator !=(const Type* ptr) const { return (mPointer != ptr); }
+ bool operator ==(const Type* ptr) const { return (mPointer == ptr); }
+ bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); }
+ bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); }
+ bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); }
+
+ LLConstPointer<Type>& operator =(const Type* ptr)
+ {
if( mPointer != ptr )
{
unref();
@@ -113,7 +228,7 @@ public:
return *this;
}
- LLPointer<Type>& operator =(const LLPointer<Type>& ptr)
+ LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr)
{
if( mPointer != ptr.mPointer )
{
@@ -126,7 +241,7 @@ public:
// support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed.
template<typename Subclass>
- LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr)
+ LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr)
{
if( mPointer != ptr.get() )
{
@@ -138,14 +253,18 @@ public:
}
// Just exchange the pointers, which will not change the reference counts.
- static void swap(LLPointer<Type>& a, LLPointer<Type>& b)
+ static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b)
{
- Type* temp = a.mPointer;
+ const Type* temp = a.mPointer;
a.mPointer = b.mPointer;
b.mPointer = temp;
}
protected:
+#ifdef LL_LIBRARY_INCLUDE
+ void ref();
+ void unref();
+#else
void ref()
{
if (mPointer)
@@ -158,19 +277,67 @@ protected:
{
if (mPointer)
{
- Type *tempp = mPointer;
+ const Type *tempp = mPointer;
mPointer = NULL;
tempp->unref();
if (mPointer != NULL)
{
- llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl;
+ LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL;
unref();
}
}
}
-
+#endif
protected:
- Type* mPointer;
+ const Type* mPointer;
+};
+
+template<typename Type>
+class LLCopyOnWritePointer : public LLPointer<Type>
+{
+public:
+ typedef LLCopyOnWritePointer<Type> self_t;
+ typedef LLPointer<Type> pointer_t;
+
+ LLCopyOnWritePointer()
+ : mStayUnique(false)
+ {}
+
+ LLCopyOnWritePointer(Type* ptr)
+ : LLPointer<Type>(ptr),
+ mStayUnique(false)
+ {}
+
+ LLCopyOnWritePointer(LLPointer<Type>& ptr)
+ : LLPointer<Type>(ptr),
+ mStayUnique(false)
+ {
+ if (ptr.mForceUnique)
+ {
+ makeUnique();
+ }
+ }
+
+ Type* write()
+ {
+ makeUnique();
+ return pointer_t::mPointer;
+ }
+
+ void makeUnique()
+ {
+ if (pointer_t::notNull() && pointer_t::mPointer->getNumRefs() > 1)
+ {
+ *(pointer_t* )(this) = new Type(*pointer_t::mPointer);
+ }
+ }
+
+ const Type* operator->() const { return pointer_t::mPointer; }
+ const Type& operator*() const { return *pointer_t::mPointer; }
+
+ void setStayUnique(bool stay) { makeUnique(); mStayUnique = stay; }
+private:
+ bool mStayUnique;
};
#endif
diff --git a/indra/llcommon/llpredicate.cpp b/indra/llcommon/llpredicate.cpp
new file mode 100644
index 0000000000..1278948e24
--- /dev/null
+++ b/indra/llcommon/llpredicate.cpp
@@ -0,0 +1,41 @@
+/**
+ * @file llpredicate.cpp
+ * @brief abstraction for filtering objects by predicates, with arbitrary boolean expressions
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#include "linden_common.h"
+
+#include "llpredicate.h"
+
+namespace LLPredicate
+{
+ const U32 cPredicateFlagsFromEnum[5] =
+ {
+ 0xAAAAaaaa, // 10101010101010101010101010101010
+ 0xCCCCcccc, // 11001100110011001100110011001100
+ 0xF0F0F0F0, // 11110000111100001111000011110000
+ 0xFF00FF00, // 11111111000000001111111100000000
+ 0xFFFF0000 // 11111111111111110000000000000000
+ };
+}
+
diff --git a/indra/llcommon/llpredicate.h b/indra/llcommon/llpredicate.h
new file mode 100644
index 0000000000..e6c56a5711
--- /dev/null
+++ b/indra/llcommon/llpredicate.h
@@ -0,0 +1,210 @@
+/**
+ * @file llpredicate.h
+ * @brief abstraction for filtering objects by predicates, with arbitrary boolean expressions
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPREDICATE_H
+#define LL_LLPREDICATE_H
+
+#include "llerror.h"
+
+namespace LLPredicate
+{
+ template<typename ENUM> class Rule;
+
+ extern const U32 cPredicateFlagsFromEnum[5];
+
+ template<typename ENUM>
+ class Value
+ {
+ public:
+ typedef U32 predicate_flag_t;
+ static const S32 cMaxEnum = 5;
+
+ Value(ENUM e, bool predicate_value = true)
+ : mPredicateFlags(predicate_value ? cPredicateFlagsFromEnum[e] : ~cPredicateFlagsFromEnum[e])
+ {
+ llassert(0 <= e && e < cMaxEnum);
+ }
+
+ Value()
+ : mPredicateFlags(0xFFFFffff)
+ {}
+
+ Value operator!() const
+ {
+ Value new_value;
+ new_value.mPredicateFlags = ~mPredicateFlags;
+ return new_value;
+ }
+
+ Value operator &&(const Value other) const
+ {
+ Value new_value;
+ new_value.mPredicateFlags = mPredicateFlags & other.mPredicateFlags;
+ return new_value;
+ }
+
+ Value operator ||(const Value other) const
+ {
+ Value new_value;
+ new_value.mPredicateFlags = mPredicateFlags | other.mPredicateFlags;
+ return new_value;
+ }
+
+ void set(ENUM e, bool value = true)
+ {
+ llassert(0 <= e && e < cMaxEnum);
+ predicate_flag_t flags_to_modify;
+ predicate_flag_t mask = cPredicateFlagsFromEnum[e];
+ if (value)
+ { // add predicate "e" to flags that don't contain it already
+ flags_to_modify = (mPredicateFlags & ~mask);
+ // clear flags not containing e
+ mPredicateFlags &= mask;
+ // add back flags shifted to contain e
+ mPredicateFlags |= flags_to_modify << (0x1 << e);
+ }
+ else
+ { // remove predicate "e" from flags that contain it
+ flags_to_modify = (mPredicateFlags & mask);
+ // clear flags containing e
+ mPredicateFlags &= ~mask;
+ // add back flags shifted to not contain e
+ mPredicateFlags |= flags_to_modify >> (0x1 << e);
+ }
+ }
+
+ void forget(ENUM e)
+ {
+ set(e, true);
+ U32 flags_with_predicate = mPredicateFlags;
+ set(e, false);
+ // ambiguous value is result of adding and removing predicate at the same time!
+ mPredicateFlags |= flags_with_predicate;
+ }
+
+ bool allSet() const
+ {
+ return mPredicateFlags == ~0;
+ }
+
+ bool noneSet() const
+ {
+ return mPredicateFlags == 0;
+ }
+
+ bool someSet() const
+ {
+ return mPredicateFlags != 0;
+ }
+
+ private:
+ predicate_flag_t mPredicateFlags;
+ };
+
+ template<typename ENUM>
+ class Rule
+ {
+ public:
+ Rule(ENUM value)
+ : mRule(value)
+ {}
+
+ Rule(const Value<ENUM> other)
+ : mRule(other)
+ {}
+
+ Rule()
+ {}
+
+ void require(ENUM e, bool match)
+ {
+ mRule.set(e, match);
+ }
+
+ void allow(ENUM e)
+ {
+ mRule.forget(e);
+ }
+
+ bool check(const Value<ENUM> value) const
+ {
+ return (mRule && value).someSet();
+ }
+
+ bool requires(const Value<ENUM> value) const
+ {
+ return (mRule && value).someSet() && (!mRule && value).noneSet();
+ }
+
+ bool isAmbivalent(const Value<ENUM> value) const
+ {
+ return (mRule && value).someSet() && (!mRule && value).someSet();
+ }
+
+ bool acceptsAll() const
+ {
+ return mRule.allSet();
+ }
+
+ bool acceptsNone() const
+ {
+ return mRule.noneSet();
+ }
+
+ Rule operator!() const
+ {
+ Rule new_rule;
+ new_rule.mRule = !mRule;
+ return new_rule;
+ }
+
+ Rule operator &&(const Rule other) const
+ {
+ Rule new_rule;
+ new_rule.mRule = mRule && other.mRule;
+ return new_rule;
+ }
+
+ Rule operator ||(const Rule other) const
+ {
+ Rule new_rule;
+ new_rule.mRule = mRule || other.mRule;
+ return new_rule;
+ }
+
+ private:
+ Value<ENUM> mRule;
+ };
+}
+
+template<typename ENUM>
+LLPredicate::Value<ENUM> ll_make_predicate(ENUM e, bool predicate_value = true)
+{
+ return LLPredicate::Value<ENUM>(e, predicate_value);
+}
+
+
+#endif // LL_LLPREDICATE_H
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
index 1c1503ca7b..2c4bcc91f6 100644..100755
--- a/indra/llcommon/llpreprocessor.h
+++ b/indra/llcommon/llpreprocessor.h
@@ -3,31 +3,25 @@
* @brief This file should be included in all Linden Lab files and
* should only contain special preprocessor directives
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -107,6 +101,11 @@
#endif
+#if LL_WINDOWS
+# define LL_THREAD_LOCAL __declspec(thread)
+#else
+# define LL_THREAD_LOCAL __thread
+#endif
// Static linking with apr on windows needs to be declared.
#if LL_WINDOWS && !LL_COMMON_LINK_SHARED
@@ -138,7 +137,7 @@
#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
#pragma warning( 3 : 4266 ) // 'function' : no override available for virtual member function from base 'type'; function is hidden
#pragma warning (disable : 4180) // qualifier applied to function type has no meaning; ignored
-#pragma warning( disable : 4284 ) // 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)
#pragma warning( disable : 4996 ) // warning: deprecated
@@ -157,6 +156,8 @@
#pragma warning (disable : 4251) // member needs to have dll-interface to be used by clients of class
#pragma warning (disable : 4275) // non dll-interface class used as base for dll-interface class
+#pragma warning (disable : 4018) // '<' : signed/unsigned mismatch
+
#endif // LL_MSVC
#if LL_WINDOWS
@@ -185,4 +186,25 @@
# define LL_COMMON_API
#endif // LL_COMMON_LINK_SHARED
+#if LL_WINDOWS
+#define LL_TYPEOF(exp) decltype(exp)
+#elif LL_LINUX
+#define LL_TYPEOF(exp) typeof(exp)
+#elif LL_DARWIN
+#define LL_TYPEOF(exp) typeof(exp)
+#endif
+
+#define LL_TO_STRING_HELPER(x) #x
+#define LL_TO_STRING(x) LL_TO_STRING_HELPER(x)
+#define LL_FILE_LINENO_MSG(msg) __FILE__ "(" LL_TO_STRING(__LINE__) ") : " msg
+#define LL_GLUE_IMPL(x, y) x##y
+#define LL_GLUE_TOKENS(x, y) LL_GLUE_IMPL(x, y)
+
+#if LL_WINDOWS
+#define LL_COMPILE_TIME_MESSAGE(msg) __pragma(message(LL_FILE_LINENO_MSG(msg)))
+#else
+// no way to get gcc 4.2 to print a user-defined diagnostic message only when a macro is used
+#define LL_COMPILE_TIME_MESSAGE(msg)
+#endif
+
#endif // not LL_LINDEN_PREPROCESSOR_H
diff --git a/indra/llcommon/llpriqueuemap.h b/indra/llcommon/llpriqueuemap.h
index a861ac1be7..d8d3edd48a 100644..100755
--- a/indra/llcommon/llpriqueuemap.h
+++ b/indra/llcommon/llpriqueuemap.h
@@ -2,31 +2,25 @@
* @file llpriqueuemap.h
* @brief Priority queue implementation
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLPRIQUEUEMAP_H
@@ -90,7 +84,7 @@ public:
pqm_iter iter = mMap.find(LLPQMKey<DATA_TYPE>(priority, data));
if (iter != mMap.end())
{
- llerrs << "Pushing already existing data onto queue!" << llendl;
+ LL_ERRS() << "Pushing already existing data onto queue!" << LL_ENDL;
}
#endif
mMap.insert(pqm_pair(LLPQMKey<DATA_TYPE>(priority, data), data));
@@ -118,14 +112,14 @@ public:
iter = mMap.find(cur_key);
if (iter == mMap.end())
{
- llwarns << "Data not on priority queue!" << llendl;
+ LL_WARNS() << "Data not on priority queue!" << LL_ENDL;
// 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;
+ LL_ERRS() << "Data on priority queue but priority not matched!" << LL_ENDL;
}
}
return;
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
new file mode 100755
index 0000000000..715df36f39
--- /dev/null
+++ b/indra/llcommon/llprocess.cpp
@@ -0,0 +1,1351 @@
+/**
+ * @file llprocess.cpp
+ * @brief Utility class for launching, terminating, and tracking the state of processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llprocess.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llsingleton.h"
+#include "llstring.h"
+#include "stringize.h"
+#include "llapr.h"
+#include "apr_signal.h"
+#include "llevents.h"
+
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/buffers_iterator.hpp>
+#include <iostream>
+#include <stdexcept>
+#include <limits>
+#include <algorithm>
+#include <vector>
+#include <typeinfo>
+#include <utility>
+
+/*****************************************************************************
+* Helpers
+*****************************************************************************/
+static const char* whichfile_[] = { "stdin", "stdout", "stderr" };
+static std::string empty;
+static LLProcess::Status interpret_status(int status);
+static std::string getDesc(const LLProcess::Params& params);
+
+static std::string whichfile(LLProcess::FILESLOT index)
+{
+ if (index < LL_ARRAY_SIZE(whichfile_))
+ return whichfile_[index];
+ return STRINGIZE("file slot " << index);
+}
+
+/**
+ * Ref-counted "mainloop" listener. As long as there are still outstanding
+ * LLProcess objects, keep listening on "mainloop" so we can keep polling APR
+ * for process status.
+ */
+class LLProcessListener
+{
+ LOG_CLASS(LLProcessListener);
+public:
+ LLProcessListener():
+ mCount(0)
+ {}
+
+ void addPoll(const LLProcess&)
+ {
+ // Unconditionally increment mCount. If it was zero before
+ // incrementing, listen on "mainloop".
+ if (mCount++ == 0)
+ {
+ LL_DEBUGS("LLProcess") << "listening on \"mainloop\"" << LL_ENDL;
+ mConnection = LLEventPumps::instance().obtain("mainloop")
+ .listen("LLProcessListener", boost::bind(&LLProcessListener::tick, this, _1));
+ }
+ }
+
+ void dropPoll(const LLProcess&)
+ {
+ // Unconditionally decrement mCount. If it's zero after decrementing,
+ // stop listening on "mainloop".
+ if (--mCount == 0)
+ {
+ LL_DEBUGS("LLProcess") << "disconnecting from \"mainloop\"" << LL_ENDL;
+ mConnection.disconnect();
+ }
+ }
+
+private:
+ /// called once per frame by the "mainloop" LLEventPump
+ bool tick(const LLSD&)
+ {
+ // Tell APR to sense whether each registered LLProcess is still
+ // running and call handle_status() appropriately. We should be able
+ // to get the same info from an apr_proc_wait(APR_NOWAIT) call; but at
+ // least in APR 1.4.2, testing suggests that even with APR_NOWAIT,
+ // apr_proc_wait() blocks the caller. We can't have that in the
+ // viewer. Hence the callback rigmarole. (Once we update APR, it's
+ // probably worth testing again.) Also -- although there's an
+ // apr_proc_other_child_refresh() call, i.e. get that information for
+ // one specific child, it accepts an 'apr_other_child_rec_t*' that's
+ // mentioned NOWHERE else in the documentation or header files! I
+ // would use the specific call in LLProcess::getStatus() if I knew
+ // how. As it is, each call to apr_proc_other_child_refresh_all() will
+ // call callbacks for ALL still-running child processes. That's why we
+ // centralize such calls, using "mainloop" to ensure it happens once
+ // per frame, and refcounting running LLProcess objects to remain
+ // registered only while needed.
+ LL_DEBUGS("LLProcess") << "calling apr_proc_other_child_refresh_all()" << LL_ENDL;
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ return false;
+ }
+
+ /// If this object is destroyed before mCount goes to zero, stop
+ /// listening on "mainloop" anyway.
+ LLTempBoundListener mConnection;
+ unsigned mCount;
+};
+static LLProcessListener sProcessListener;
+
+/*****************************************************************************
+* WritePipe and ReadPipe
+*****************************************************************************/
+LLProcess::BasePipe::~BasePipe() {}
+const LLProcess::BasePipe::size_type
+ // use funky syntax to call max() to avoid blighted max() macros
+ LLProcess::BasePipe::npos((std::numeric_limits<LLProcess::BasePipe::size_type>::max)());
+
+class WritePipeImpl: public LLProcess::WritePipe
+{
+ LOG_CLASS(WritePipeImpl);
+public:
+ WritePipeImpl(const std::string& desc, apr_file_t* pipe):
+ mDesc(desc),
+ mPipe(pipe),
+ // Essential to initialize our std::ostream with our special streambuf!
+ mStream(&mStreambuf)
+ {
+ mConnection = LLEventPumps::instance().obtain("mainloop")
+ .listen(LLEventPump::inventName("WritePipe"),
+ boost::bind(&WritePipeImpl::tick, this, _1));
+
+#if ! LL_WINDOWS
+ // We can't count on every child process reading everything we try to
+ // write to it. And if the child terminates with WritePipe data still
+ // pending, unless we explicitly suppress it, Posix will hit us with
+ // SIGPIPE. That would terminate the viewer, boom. "Ignoring" it means
+ // APR gets the correct errno, passes it back to us, we log it, etc.
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ }
+
+ virtual std::ostream& get_ostream() { return mStream; }
+ virtual size_type size() const { return mStreambuf.size(); }
+
+ bool tick(const LLSD&)
+ {
+ typedef boost::asio::streambuf::const_buffers_type const_buffer_sequence;
+ // If there's anything to send, try to send it.
+ std::size_t total(mStreambuf.size()), consumed(0);
+ if (total)
+ {
+ const_buffer_sequence bufs = mStreambuf.data();
+ // In general, our streambuf might contain a number of different
+ // physical buffers; iterate over those.
+ bool keepwriting = true;
+ for (const_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end());
+ bufi != bufend && keepwriting; ++bufi)
+ {
+ // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents
+ // Although apr_file_write() accepts const void*, we
+ // manipulate const char* so we can increment the pointer.
+ const char* remainptr = boost::asio::buffer_cast<const char*>(*bufi);
+ std::size_t remainlen = boost::asio::buffer_size(*bufi);
+ while (remainlen)
+ {
+ // Tackle the current buffer in discrete chunks. On
+ // Windows, we've observed strange failures when trying to
+ // write big lengths (~1 MB) in a single operation. Even a
+ // 32K chunk seems too large. At some point along the way
+ // apr_file_write() returns 11 (Resource temporarily
+ // unavailable, i.e. EAGAIN) and says it wrote 0 bytes --
+ // even though it did write the chunk! Our next write
+ // attempt retries with the same chunk, resulting in the
+ // chunk being duplicated at the child end. Using smaller
+ // chunks is empirically more reliable.
+ std::size_t towrite((std::min)(remainlen, std::size_t(4*1024)));
+ apr_size_t written(towrite);
+ apr_status_t err = apr_file_write(mPipe, remainptr, &written);
+ // EAGAIN is exactly what we want from a nonblocking pipe.
+ // Rather than waiting for data, it should return immediately.
+ if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err)))
+ {
+ LL_WARNS("LLProcess") << "apr_file_write(" << towrite << ") on " << mDesc
+ << " got " << err << ":" << LL_ENDL;
+ ll_apr_warn_status(err);
+ }
+
+ // 'written' is modified to reflect the number of bytes actually
+ // written. Make sure we consume those later. (Don't consume them
+ // now, that would invalidate the buffer iterator sequence!)
+ consumed += written;
+ // don't forget to advance to next chunk of current buffer
+ remainptr += written;
+ remainlen -= written;
+
+ char msgbuf[512];
+ LL_DEBUGS("LLProcess") << "wrote " << written << " of " << towrite
+ << " bytes to " << mDesc
+ << " (original " << total << "),"
+ << " code " << err << ": "
+ << apr_strerror(err, msgbuf, sizeof(msgbuf))
+ << LL_ENDL;
+
+ // The parent end of this pipe is nonblocking. If we weren't able
+ // to write everything we wanted, don't keep banging on it -- that
+ // won't change until the child reads some. Wait for next tick().
+ if (written < towrite)
+ {
+ keepwriting = false; // break outer loop over buffers too
+ break;
+ }
+ } // next chunk of current buffer
+ } // next buffer
+ // In all, we managed to write 'consumed' bytes. Remove them from the
+ // streambuf so we don't keep trying to send them. This could be
+ // anywhere from 0 up to mStreambuf.size(); anything we haven't yet
+ // sent, we'll try again later.
+ mStreambuf.consume(consumed);
+ }
+
+ return false;
+ }
+
+private:
+ std::string mDesc;
+ apr_file_t* mPipe;
+ LLTempBoundListener mConnection;
+ boost::asio::streambuf mStreambuf;
+ std::ostream mStream;
+};
+
+class ReadPipeImpl: public LLProcess::ReadPipe
+{
+ LOG_CLASS(ReadPipeImpl);
+public:
+ ReadPipeImpl(const std::string& desc, apr_file_t* pipe, LLProcess::FILESLOT index):
+ mDesc(desc),
+ mPipe(pipe),
+ mIndex(index),
+ // Essential to initialize our std::istream with our special streambuf!
+ mStream(&mStreambuf),
+ mPump("ReadPipe", true), // tweak name as needed to avoid collisions
+ mLimit(0),
+ mEOF(false)
+ {
+ mConnection = LLEventPumps::instance().obtain("mainloop")
+ .listen(LLEventPump::inventName("ReadPipe"),
+ boost::bind(&ReadPipeImpl::tick, this, _1));
+ }
+
+ // Much of the implementation is simply connecting the abstract virtual
+ // methods with implementation data concealed from the base class.
+ virtual std::istream& get_istream() { return mStream; }
+ virtual std::string getline() { return LLProcess::getline(mStream); }
+ virtual LLEventPump& getPump() { return mPump; }
+ virtual void setLimit(size_type limit) { mLimit = limit; }
+ virtual size_type getLimit() const { return mLimit; }
+ virtual size_type size() const { return mStreambuf.size(); }
+
+ virtual std::string read(size_type len)
+ {
+ // Read specified number of bytes into a buffer.
+ size_type readlen((std::min)(size(), len));
+ // Formally, &buffer[0] is invalid for a vector of size() 0. Exit
+ // early in that situation.
+ if (! readlen)
+ return "";
+ // Make a buffer big enough.
+ std::vector<char> buffer(readlen);
+ mStream.read(&buffer[0], readlen);
+ // Since we've already clamped 'readlen', we can think of no reason
+ // why mStream.read() should read fewer than 'readlen' bytes.
+ // Nonetheless, use the actual retrieved length.
+ return std::string(&buffer[0], mStream.gcount());
+ }
+
+ virtual std::string peek(size_type offset=0, size_type len=npos) const
+ {
+ // Constrain caller's offset and len to overlap actual buffer content.
+ std::size_t real_offset = (std::min)(mStreambuf.size(), std::size_t(offset));
+ size_type want_end = (len == npos)? npos : (real_offset + len);
+ std::size_t real_end = (std::min)(mStreambuf.size(), std::size_t(want_end));
+ boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+ return std::string(boost::asio::buffers_begin(cbufs) + real_offset,
+ boost::asio::buffers_begin(cbufs) + real_end);
+ }
+
+ virtual size_type find(const std::string& seek, size_type offset=0) const
+ {
+ // If we're passing a string of length 1, use find(char), which can
+ // use an O(n) std::find() rather than the O(n^2) std::search().
+ if (seek.length() == 1)
+ {
+ return find(seek[0], offset);
+ }
+
+ // If offset is beyond the whole buffer, can't even construct a valid
+ // iterator range; can't possibly find the string we seek.
+ if (offset > mStreambuf.size())
+ {
+ return npos;
+ }
+
+ boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+ boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type>
+ begin(boost::asio::buffers_begin(cbufs)),
+ end (boost::asio::buffers_end(cbufs)),
+ found(std::search(begin + offset, end, seek.begin(), seek.end()));
+ return (found == end)? npos : (found - begin);
+ }
+
+ virtual size_type find(char seek, size_type offset=0) const
+ {
+ // If offset is beyond the whole buffer, can't even construct a valid
+ // iterator range; can't possibly find the char we seek.
+ if (offset > mStreambuf.size())
+ {
+ return npos;
+ }
+
+ boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+ boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type>
+ begin(boost::asio::buffers_begin(cbufs)),
+ end (boost::asio::buffers_end(cbufs)),
+ found(std::find(begin + offset, end, seek));
+ return (found == end)? npos : (found - begin);
+ }
+
+ bool tick(const LLSD&)
+ {
+ // Once we've hit EOF, skip all the rest of this.
+ if (mEOF)
+ return false;
+
+ typedef boost::asio::streambuf::mutable_buffers_type mutable_buffer_sequence;
+ // Try, every time, to read into our streambuf. In fact, we have no
+ // idea how much data the child might be trying to send: keep trying
+ // until we're convinced we've temporarily exhausted the pipe.
+ enum PipeState { RETRY, EXHAUSTED, CLOSED };
+ PipeState state = RETRY;
+ std::size_t committed(0);
+ do
+ {
+ // attempt to read an arbitrary size
+ mutable_buffer_sequence bufs = mStreambuf.prepare(4096);
+ // In general, the mutable_buffer_sequence returned by prepare() might
+ // contain a number of different physical buffers; iterate over those.
+ std::size_t tocommit(0);
+ for (mutable_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end());
+ bufi != bufend; ++bufi)
+ {
+ // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents
+ std::size_t toread(boost::asio::buffer_size(*bufi));
+ apr_size_t gotten(toread);
+ apr_status_t err = apr_file_read(mPipe,
+ boost::asio::buffer_cast<void*>(*bufi),
+ &gotten);
+ // EAGAIN is exactly what we want from a nonblocking pipe.
+ // Rather than waiting for data, it should return immediately.
+ if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err)))
+ {
+ // Handle EOF specially: it's part of normal-case processing.
+ if (err == APR_EOF)
+ {
+ LL_DEBUGS("LLProcess") << "EOF on " << mDesc << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("LLProcess") << "apr_file_read(" << toread << ") on " << mDesc
+ << " got " << err << ":" << LL_ENDL;
+ ll_apr_warn_status(err);
+ }
+ // Either way, though, we won't need any more tick() calls.
+ mConnection.disconnect();
+ // Ignore any subsequent calls we might get anyway.
+ mEOF = true;
+ state = CLOSED; // also break outer retry loop
+ break;
+ }
+
+ // 'gotten' was modified to reflect the number of bytes actually
+ // received. Make sure we commit those later. (Don't commit them
+ // now, that would invalidate the buffer iterator sequence!)
+ tocommit += gotten;
+ LL_DEBUGS("LLProcess") << "filled " << gotten << " of " << toread
+ << " bytes from " << mDesc << LL_ENDL;
+
+ // The parent end of this pipe is nonblocking. If we weren't even
+ // able to fill this buffer, don't loop to try to fill the next --
+ // that won't change until the child writes more. Wait for next
+ // tick().
+ if (gotten < toread)
+ {
+ // break outer retry loop too
+ state = EXHAUSTED;
+ break;
+ }
+ }
+
+ // Don't forget to "commit" the data!
+ mStreambuf.commit(tocommit);
+ committed += tocommit;
+
+ // state is changed from RETRY when we can't fill any one buffer
+ // of the mutable_buffer_sequence established by the current
+ // prepare() call -- whether due to error or not enough bytes.
+ // That is, if state is still RETRY, we've filled every physical
+ // buffer in the mutable_buffer_sequence. In that case, for all we
+ // know, the child might have still more data pending -- go for it!
+ } while (state == RETRY);
+
+ // Once we recognize that the pipe is closed, make one more call to
+ // listener. The listener might be waiting for a particular substring
+ // to arrive, or a particular length of data or something. The event
+ // with "eof" == true announces that nothing further will arrive, so
+ // use it or lose it.
+ if (committed || state == CLOSED)
+ {
+ // If we actually received new data, publish it on our LLEventPump
+ // as advertised. Constrain it by mLimit. But show listener the
+ // actual accumulated buffer size, regardless of mLimit.
+ size_type datasize((std::min)(mLimit, size_type(mStreambuf.size())));
+ mPump.post(LLSDMap
+ ("data", peek(0, datasize))
+ ("len", LLSD::Integer(mStreambuf.size()))
+ ("slot", LLSD::Integer(mIndex))
+ ("name", whichfile(mIndex))
+ ("desc", mDesc)
+ ("eof", state == CLOSED));
+ }
+
+ return false;
+ }
+
+private:
+ std::string mDesc;
+ apr_file_t* mPipe;
+ LLProcess::FILESLOT mIndex;
+ LLTempBoundListener mConnection;
+ boost::asio::streambuf mStreambuf;
+ std::istream mStream;
+ LLEventStream mPump;
+ size_type mLimit;
+ bool mEOF;
+};
+
+/*****************************************************************************
+* LLProcess itself
+*****************************************************************************/
+/// Need an exception to avoid constructing an invalid LLProcess object, but
+/// internal use only
+struct LLProcessError: public std::runtime_error
+{
+ LLProcessError(const std::string& msg): std::runtime_error(msg) {}
+};
+
+LLProcessPtr LLProcess::create(const LLSDOrParams& params)
+{
+ try
+ {
+ return LLProcessPtr(new LLProcess(params));
+ }
+ catch (const LLProcessError& e)
+ {
+ LL_WARNS("LLProcess") << e.what() << LL_ENDL;
+
+ // If caller is requesting an event on process termination, send one
+ // indicating bad launch. This may prevent someone waiting forever for
+ // a termination post that can't arrive because the child never
+ // started.
+ if (params.postend.isProvided())
+ {
+ LLEventPumps::instance().obtain(params.postend)
+ .post(LLSDMap
+ // no "id"
+ ("desc", getDesc(params))
+ ("state", LLProcess::UNSTARTED)
+ // no "data"
+ ("string", e.what())
+ );
+ }
+
+ return LLProcessPtr();
+ }
+}
+
+/// Call an apr function returning apr_status_t. On failure, log warning and
+/// throw LLProcessError mentioning the function call that produced that
+/// result.
+#define chkapr(func) \
+ if (ll_apr_warn_status(func)) \
+ throw LLProcessError(#func " failed")
+
+LLProcess::LLProcess(const LLSDOrParams& params):
+ mAutokill(params.autokill),
+ mPipes(NSLOTS)
+{
+ // Hmm, when you construct a ptr_vector with a size, it merely reserves
+ // space, it doesn't actually make it that big. Explicitly make it bigger.
+ // Because of ptr_vector's odd semantics, have to push_back(0) the right
+ // number of times! resize() wants to default-construct new BasePipe
+ // instances, which fails because it's pure virtual. But because of the
+ // constructor call, these push_back() calls should require no new
+ // allocation.
+ for (size_t i = 0; i < mPipes.capacity(); ++i)
+ mPipes.push_back(0);
+
+ if (! params.validateBlock(true))
+ {
+ throw LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
+ << LLSDNotationStreamer(params)));
+ }
+
+ mPostend = params.postend;
+
+ apr_procattr_t *procattr = NULL;
+ chkapr(apr_procattr_create(&procattr, gAPRPoolp));
+
+ // IQA-490, CHOP-900: On Windows, ask APR to jump through hoops to
+ // constrain the set of handles passed to the child process. Before we
+ // changed to APR, the Windows implementation of LLProcessLauncher called
+ // CreateProcess(bInheritHandles=FALSE), meaning to pass NO open handles
+ // to the child process. Now that we support pipes, though, we must allow
+ // apr_proc_create() to pass bInheritHandles=TRUE. But without taking
+ // special pains, that causes trouble in a number of ways, due to the fact
+ // that the viewer is constantly opening and closing files -- most of
+ // which CreateProcess() passes to every child process!
+#if ! defined(APR_HAS_PROCATTR_CONSTRAIN_HANDLE_SET)
+ // Our special preprocessor symbol isn't even defined -- wrong APR
+ LL_WARNS("LLProcess") << "This version of APR lacks Linden "
+ << "apr_procattr_constrain_handle_set() extension" << LL_ENDL;
+#else
+ chkapr(apr_procattr_constrain_handle_set(procattr, 1));
+#endif
+
+ // For which of stdin, stdout, stderr should we create a pipe to the
+ // child? In the viewer, there are only a couple viable
+ // apr_procattr_io_set() alternatives: inherit the viewer's own stdxxx
+ // handle (APR_NO_PIPE, e.g. for stdout, stderr), or create a pipe that's
+ // blocking on the child end but nonblocking at the viewer end
+ // (APR_CHILD_BLOCK).
+ // Other major options could include explicitly creating a single APR pipe
+ // and passing it as both stdout and stderr (apr_procattr_child_out_set(),
+ // apr_procattr_child_err_set()), or accepting a filename, opening it and
+ // passing that apr_file_t (simple <, >, 2> redirect emulation).
+ std::vector<apr_int32_t> select;
+ BOOST_FOREACH(const FileParam& fparam, params.files)
+ {
+ // Every iteration, we're going to append an item to 'select'. At the
+ // top of the loop, its size() is, in effect, an index. Use that to
+ // pick a string description for messages.
+ std::string which(whichfile(FILESLOT(select.size())));
+ if (fparam.type().empty()) // inherit our file descriptor
+ {
+ select.push_back(APR_NO_PIPE);
+ }
+ else if (fparam.type() == "pipe") // anonymous pipe
+ {
+ if (! fparam.name().empty())
+ {
+ LL_WARNS("LLProcess") << "For " << params.executable()
+ << ": internal names for reusing pipes ('"
+ << fparam.name() << "' for " << which
+ << ") are not yet supported -- creating distinct pipe"
+ << LL_ENDL;
+ }
+ // The viewer can't block for anything: the parent end MUST be
+ // nonblocking. As the APR documentation itself points out, it
+ // makes very little sense to set nonblocking I/O for the child
+ // end of a pipe: only a specially-written child could deal with
+ // that.
+ select.push_back(APR_CHILD_BLOCK);
+ }
+ else
+ {
+ throw LLProcessError(STRINGIZE("For " << params.executable()
+ << ": unsupported FileParam for " << which
+ << ": type='" << fparam.type()
+ << "', name='" << fparam.name() << "'"));
+ }
+ }
+ // By default, pass APR_NO_PIPE for unspecified slots.
+ while (select.size() < NSLOTS)
+ {
+ select.push_back(APR_NO_PIPE);
+ }
+ chkapr(apr_procattr_io_set(procattr, select[STDIN], select[STDOUT], select[STDERR]));
+
+ // Thumbs down on implicitly invoking the shell to invoke the child. From
+ // our point of view, the other major alternative to APR_PROGRAM_PATH
+ // would be APR_PROGRAM_ENV: still copy environment, but require full
+ // executable pathname. I don't see a downside to searching the PATH,
+ // though: if our caller wants (e.g.) a specific Python interpreter, s/he
+ // can still pass the full pathname.
+ chkapr(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+ // YES, do extra work if necessary to report child exec() failures back to
+ // parent process.
+ chkapr(apr_procattr_error_check_set(procattr, 1));
+ // Do not start a non-autokill child in detached state. On Posix
+ // platforms, this setting attempts to daemonize the new child, closing
+ // std handles and the like, and that's a bit more detachment than we
+ // want. autokill=false just means not to implicitly kill the child when
+ // the parent terminates!
+// chkapr(apr_procattr_detach_set(procattr, params.autokill? 0 : 1));
+
+ if (params.autokill)
+ {
+#if ! defined(APR_HAS_PROCATTR_AUTOKILL_SET)
+ // Our special preprocessor symbol isn't even defined -- wrong APR
+ LL_WARNS("LLProcess") << "This version of APR lacks Linden apr_procattr_autokill_set() extension" << LL_ENDL;
+#elif ! APR_HAS_PROCATTR_AUTOKILL_SET
+ // Symbol is defined, but to 0: expect apr_procattr_autokill_set() to
+ // return APR_ENOTIMPL.
+#else // APR_HAS_PROCATTR_AUTOKILL_SET nonzero
+ ll_apr_warn_status(apr_procattr_autokill_set(procattr, 1));
+#endif
+ }
+
+ // In preparation for calling apr_proc_create(), we collect a number of
+ // const char* pointers obtained from std::string::c_str(). Turns out
+ // LLInitParam::Block's helpers Optional, Mandatory, Multiple et al.
+ // guarantee that converting to the wrapped type (std::string in our
+ // case), e.g. by calling operator(), returns a reference to *the same
+ // instance* of the wrapped type that's stored in our Block subclass.
+ // That's important! We know 'params' persists throughout this method
+ // call; but without that guarantee, we'd have to assume that converting
+ // one of its members to std::string might return a different (temp)
+ // instance. Capturing the c_str() from a temporary std::string is Bad Bad
+ // Bad. But armed with this knowledge, when you see params.cwd().c_str(),
+ // grit your teeth and smile and carry on.
+
+ if (params.cwd.isProvided())
+ {
+ chkapr(apr_procattr_dir_set(procattr, params.cwd().c_str()));
+ }
+
+ // create an argv vector for the child process
+ std::vector<const char*> argv;
+
+ // Add the executable path. See above remarks about c_str().
+ argv.push_back(params.executable().c_str());
+
+ // Add arguments. See above remarks about c_str().
+ BOOST_FOREACH(const std::string& arg, params.args)
+ {
+ argv.push_back(arg.c_str());
+ }
+
+ // terminate with a null pointer
+ argv.push_back(NULL);
+
+ // Launch! The NULL would be the environment block, if we were passing
+ // one. Hand-expand chkapr() macro so we can fill in the actual command
+ // string instead of the variable names.
+ if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
+ gAPRPoolp)))
+ {
+ throw LLProcessError(STRINGIZE(params << " failed"));
+ }
+
+ // arrange to call status_callback()
+ apr_proc_other_child_register(&mProcess, &LLProcess::status_callback, this, mProcess.in,
+ gAPRPoolp);
+ // and make sure we poll it once per "mainloop" tick
+ sProcessListener.addPoll(*this);
+ mStatus.mState = RUNNING;
+
+ mDesc = STRINGIZE(getDesc(params) << " (" << mProcess.pid << ')');
+ LL_INFOS("LLProcess") << mDesc << ": launched " << params << LL_ENDL;
+
+ // Unless caller explicitly turned off autokill (child should persist),
+ // take steps to terminate the child. This is all suspenders-and-belt: in
+ // theory our destructor should kill an autokill child, but in practice
+ // that doesn't always work (e.g. VWR-21538).
+ if (params.autokill)
+ {
+/*==========================================================================*|
+ // NO: There may be an APR bug, not sure -- but at least on Mac, when
+ // gAPRPoolp is destroyed, OUR process receives SIGTERM! Apparently
+ // either our own PID is getting into the list of processes to kill()
+ // (unlikely), or somehow one of those PIDs is getting zeroed first,
+ // so that kill() sends SIGTERM to the whole process group -- this
+ // process included. I'd have to build and link with a debug version
+ // of APR to know for sure. It's too bad: this mechanism would be just
+ // right for dealing with static autokill LLProcessPtr variables,
+ // which aren't destroyed until after APR is no longer available.
+
+ // Tie the lifespan of this child process to the lifespan of our APR
+ // pool: on destruction of the pool, forcibly kill the process. Tell
+ // APR to try SIGTERM and wait 3 seconds. If that didn't work, use
+ // SIGKILL.
+ apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT);
+|*==========================================================================*/
+
+ // On Windows, associate the new child process with our Job Object.
+ autokill();
+ }
+
+ // Instantiate the proper pipe I/O machinery
+ // want to be able to point to apr_proc_t::in, out, err by index
+ typedef apr_file_t* apr_proc_t::*apr_proc_file_ptr;
+ static apr_proc_file_ptr members[] =
+ { &apr_proc_t::in, &apr_proc_t::out, &apr_proc_t::err };
+ for (size_t i = 0; i < NSLOTS; ++i)
+ {
+ if (select[i] != APR_CHILD_BLOCK)
+ continue;
+ std::string desc(STRINGIZE(mDesc << ' ' << whichfile(FILESLOT(i))));
+ apr_file_t* pipe(mProcess.*(members[i]));
+ if (i == STDIN)
+ {
+ mPipes.replace(i, new WritePipeImpl(desc, pipe));
+ }
+ else
+ {
+ mPipes.replace(i, new ReadPipeImpl(desc, pipe, FILESLOT(i)));
+ }
+ LL_DEBUGS("LLProcess") << "Instantiating " << typeid(mPipes[i]).name()
+ << "('" << desc << "')" << LL_ENDL;
+ }
+}
+
+// Helper to obtain a description string, given a Params block
+static std::string getDesc(const LLProcess::Params& params)
+{
+ // If caller specified a description string, by all means use it.
+ if (params.desc.isProvided())
+ return params.desc;
+
+ // Caller didn't say. Use the executable name -- but use just the filename
+ // part. On Mac, for instance, full pathnames get cumbersome.
+ return LLProcess::basename(params.executable);
+}
+
+//static
+std::string LLProcess::basename(const std::string& path)
+{
+ // If there are Linden utility functions to manipulate pathnames, I
+ // haven't found them -- and for this usage, Boost.Filesystem seems kind
+ // of heavyweight.
+ std::string::size_type delim = path.find_last_of("\\/");
+ // If path contains no pathname delimiters, return the whole thing.
+ if (delim == std::string::npos)
+ return path;
+
+ // Return just the part beyond the last delimiter.
+ return path.substr(delim + 1);
+}
+
+LLProcess::~LLProcess()
+{
+ // In the Linden viewer, there's at least one static LLProcessPtr. Its
+ // destructor will be called *after* ll_cleanup_apr(). In such a case,
+ // unregistering is pointless (and fatal!) -- and kill(), which also
+ // relies on APR, is impossible.
+ if (! gAPRPoolp)
+ return;
+
+ // Only in state RUNNING are we registered for callback. In UNSTARTED we
+ // haven't yet registered. And since receiving the callback is the only
+ // way we detect child termination, we only change from state RUNNING at
+ // the same time we unregister.
+ if (mStatus.mState == RUNNING)
+ {
+ // We're still registered for a callback: unregister. Do it before
+ // we even issue the kill(): even if kill() somehow prompted an
+ // instantaneous callback (unlikely), this object is going away! Any
+ // information updated in this object by such a callback is no longer
+ // available to any consumer anyway.
+ apr_proc_other_child_unregister(this);
+ // One less LLProcess to poll for
+ sProcessListener.dropPoll(*this);
+ }
+
+ if (mAutokill)
+ {
+ kill("destructor");
+ }
+}
+
+bool LLProcess::kill(const std::string& who)
+{
+ if (isRunning())
+ {
+ LL_INFOS("LLProcess") << who << " killing " << mDesc << LL_ENDL;
+
+#if LL_WINDOWS
+ int sig = -1;
+#else // Posix
+ int sig = SIGTERM;
+#endif
+
+ ll_apr_warn_status(apr_proc_kill(&mProcess, sig));
+ }
+
+ return ! isRunning();
+}
+
+//static
+bool LLProcess::kill(const LLProcessPtr& p, const std::string& who)
+{
+ if (! p)
+ return true; // process dead! (was never running)
+ return p->kill(who);
+}
+
+bool LLProcess::isRunning() const
+{
+ return getStatus().mState == RUNNING;
+}
+
+//static
+bool LLProcess::isRunning(const LLProcessPtr& p)
+{
+ if (! p)
+ return false;
+ return p->isRunning();
+}
+
+LLProcess::Status LLProcess::getStatus() const
+{
+ return mStatus;
+}
+
+//static
+LLProcess::Status LLProcess::getStatus(const LLProcessPtr& p)
+{
+ if (! p)
+ {
+ // default-constructed Status has mState == UNSTARTED
+ return Status();
+ }
+ return p->getStatus();
+}
+
+std::string LLProcess::getStatusString() const
+{
+ return getStatusString(getStatus());
+}
+
+std::string LLProcess::getStatusString(const Status& status) const
+{
+ return getStatusString(mDesc, status);
+}
+
+//static
+std::string LLProcess::getStatusString(const std::string& desc, const LLProcessPtr& p)
+{
+ if (! p)
+ {
+ // default-constructed Status has mState == UNSTARTED
+ return getStatusString(desc, Status());
+ }
+ return desc + " " + p->getStatusString();
+}
+
+//static
+std::string LLProcess::getStatusString(const std::string& desc, const Status& status)
+{
+ if (status.mState == UNSTARTED)
+ return desc + " was never launched";
+
+ if (status.mState == RUNNING)
+ return desc + " running";
+
+ if (status.mState == EXITED)
+ return STRINGIZE(desc << " exited with code " << status.mData);
+
+ if (status.mState == KILLED)
+#if LL_WINDOWS
+ return STRINGIZE(desc << " killed with exception " << std::hex << status.mData);
+#else
+ return STRINGIZE(desc << " killed by signal " << status.mData
+ << " (" << apr_signal_description_get(status.mData) << ")");
+#endif
+
+ return STRINGIZE(desc << " in unknown state " << status.mState << " (" << status.mData << ")");
+}
+
+// Classic-C-style APR callback
+void LLProcess::status_callback(int reason, void* data, int status)
+{
+ // Our only role is to bounce this static method call back into object
+ // space.
+ static_cast<LLProcess*>(data)->handle_status(reason, status);
+}
+
+#define tabent(symbol) { symbol, #symbol }
+static struct ReasonCode
+{
+ int code;
+ const char* name;
+} reasons[] =
+{
+ tabent(APR_OC_REASON_DEATH),
+ tabent(APR_OC_REASON_UNWRITABLE),
+ tabent(APR_OC_REASON_RESTART),
+ tabent(APR_OC_REASON_UNREGISTER),
+ tabent(APR_OC_REASON_LOST),
+ tabent(APR_OC_REASON_RUNNING)
+};
+#undef tabent
+
+// Object-oriented callback
+void LLProcess::handle_status(int reason, int status)
+{
+ {
+ // This odd appearance of LL_DEBUGS is just to bracket a lookup that will
+ // only be performed if in fact we're going to produce the log message.
+ LL_DEBUGS("LLProcess") << empty;
+ std::string reason_str;
+ BOOST_FOREACH(const ReasonCode& rcp, reasons)
+ {
+ if (reason == rcp.code)
+ {
+ reason_str = rcp.name;
+ break;
+ }
+ }
+ if (reason_str.empty())
+ {
+ reason_str = STRINGIZE("unknown reason " << reason);
+ }
+ LL_CONT << mDesc << ": handle_status(" << reason_str << ", " << status << ")" << LL_ENDL;
+ }
+
+ if (! (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST))
+ {
+ // We're only interested in the call when the child terminates.
+ return;
+ }
+
+ // Somewhat oddly, APR requires that you explicitly unregister even when
+ // it already knows the child has terminated. We must pass the same 'data'
+ // pointer as for the register() call, which was our 'this'.
+ apr_proc_other_child_unregister(this);
+ // don't keep polling for a terminated process
+ sProcessListener.dropPoll(*this);
+ // We overload mStatus.mState to indicate whether the child is registered
+ // for APR callback: only RUNNING means registered. Track that we've
+ // unregistered. We know the child has terminated; might be EXITED or
+ // KILLED; refine below.
+ mStatus.mState = EXITED;
+
+ // Make last-gasp calls for each of the ReadPipes we have on hand. Since
+ // they're listening on "mainloop", we can be sure they'll eventually
+ // collect all pending data from the child. But we want to be able to
+ // guarantee to our consumer that by the time we post on the "postend"
+ // LLEventPump, our ReadPipes are already buffering all the data there
+ // will ever be from the child. That lets the "postend" listener decide
+ // what to do with that final data.
+ for (size_t i = 0; i < mPipes.size(); ++i)
+ {
+ std::string error;
+ ReadPipeImpl* ppipe = getPipePtr<ReadPipeImpl>(error, FILESLOT(i));
+ if (ppipe)
+ {
+ static LLSD trivial;
+ ppipe->tick(trivial);
+ }
+ }
+
+// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
+ // It's just wrong to call apr_proc_wait() here. The only way APR knows to
+ // call us with APR_OC_REASON_DEATH is that it's already reaped this child
+ // process, so calling wait() will only produce "huh?" from the OS. We
+ // must rely on the status param passed in, which unfortunately comes
+ // straight from the OS wait() call, which means we have to decode it by
+ // hand.
+ mStatus = interpret_status(status);
+ LL_INFOS("LLProcess") << getStatusString() << LL_ENDL;
+
+ // If caller requested notification on child termination, send it.
+ if (! mPostend.empty())
+ {
+ LLEventPumps::instance().obtain(mPostend)
+ .post(LLSDMap
+ ("id", getProcessID())
+ ("desc", mDesc)
+ ("state", mStatus.mState)
+ ("data", mStatus.mData)
+ ("string", getStatusString())
+ );
+ }
+}
+
+LLProcess::id LLProcess::getProcessID() const
+{
+ return mProcess.pid;
+}
+
+LLProcess::handle LLProcess::getProcessHandle() const
+{
+#if LL_WINDOWS
+ return mProcess.hproc;
+#else
+ return mProcess.pid;
+#endif
+}
+
+std::string LLProcess::getPipeName(FILESLOT) const
+{
+ // LLProcess::FileParam::type "npipe" is not yet implemented
+ return "";
+}
+
+template<class PIPETYPE>
+PIPETYPE* LLProcess::getPipePtr(std::string& error, FILESLOT slot)
+{
+ if (slot >= NSLOTS)
+ {
+ error = STRINGIZE(mDesc << " has no slot " << slot);
+ return NULL;
+ }
+ if (mPipes.is_null(slot))
+ {
+ error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a monitored pipe");
+ return NULL;
+ }
+ // Make sure we dynamic_cast in pointer domain so we can test, rather than
+ // accepting runtime's exception.
+ PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(&mPipes[slot]);
+ if (! ppipe)
+ {
+ error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a " << typeid(PIPETYPE).name());
+ return NULL;
+ }
+
+ error.clear();
+ return ppipe;
+}
+
+template <class PIPETYPE>
+PIPETYPE& LLProcess::getPipe(FILESLOT slot)
+{
+ std::string error;
+ PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
+ if (! wp)
+ {
+ throw NoPipe(error);
+ }
+ return *wp;
+}
+
+template <class PIPETYPE>
+boost::optional<PIPETYPE&> LLProcess::getOptPipe(FILESLOT slot)
+{
+ std::string error;
+ PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
+ if (! wp)
+ {
+ LL_DEBUGS("LLProcess") << error << LL_ENDL;
+ return boost::optional<PIPETYPE&>();
+ }
+ return *wp;
+}
+
+LLProcess::WritePipe& LLProcess::getWritePipe(FILESLOT slot)
+{
+ return getPipe<WritePipe>(slot);
+}
+
+boost::optional<LLProcess::WritePipe&> LLProcess::getOptWritePipe(FILESLOT slot)
+{
+ return getOptPipe<WritePipe>(slot);
+}
+
+LLProcess::ReadPipe& LLProcess::getReadPipe(FILESLOT slot)
+{
+ return getPipe<ReadPipe>(slot);
+}
+
+boost::optional<LLProcess::ReadPipe&> LLProcess::getOptReadPipe(FILESLOT slot)
+{
+ return getOptPipe<ReadPipe>(slot);
+}
+
+//static
+std::string LLProcess::getline(std::istream& in)
+{
+ std::string line;
+ std::getline(in, line);
+ // Blur the distinction between "\r\n" and plain "\n". std::getline() will
+ // have eaten the "\n", but we could still end up with a trailing "\r".
+ std::string::size_type lastpos = line.find_last_not_of("\r");
+ if (lastpos != std::string::npos)
+ {
+ // Found at least one character that's not a trailing '\r'. SKIP OVER
+ // IT and erase the rest of the line.
+ line.erase(lastpos+1);
+ }
+ return line;
+}
+
+std::ostream& operator<<(std::ostream& out, const LLProcess::Params& params)
+{
+ if (params.cwd.isProvided())
+ {
+ out << "cd " << LLStringUtil::quote(params.cwd) << ": ";
+ }
+ out << LLStringUtil::quote(params.executable);
+ BOOST_FOREACH(const std::string& arg, params.args)
+ {
+ out << ' ' << LLStringUtil::quote(arg);
+ }
+ return out;
+}
+
+/*****************************************************************************
+* Windows specific
+*****************************************************************************/
+#if LL_WINDOWS
+
+static std::string WindowsErrorString(const std::string& operation);
+
+void LLProcess::autokill()
+{
+ // hopefully now handled by apr_procattr_autokill_set()
+}
+
+LLProcess::handle LLProcess::isRunning(handle h, const std::string& desc)
+{
+ // This direct Windows implementation is because we have no access to the
+ // apr_proc_t struct: we expect it's been destroyed.
+ if (! h)
+ return 0;
+
+ DWORD waitresult = WaitForSingleObject(h, 0);
+ if(waitresult == WAIT_OBJECT_0)
+ {
+ // the process has completed.
+ if (! desc.empty())
+ {
+ DWORD status = 0;
+ if (! GetExitCodeProcess(h, &status))
+ {
+ LL_WARNS("LLProcess") << desc << " terminated, but "
+ << WindowsErrorString("GetExitCodeProcess()") << LL_ENDL;
+ }
+ {
+ LL_INFOS("LLProcess") << getStatusString(desc, interpret_status(status))
+ << LL_ENDL;
+ }
+ }
+ CloseHandle(h);
+ return 0;
+ }
+
+ return h;
+}
+
+static LLProcess::Status interpret_status(int status)
+{
+ LLProcess::Status result;
+
+ // This bit of code is cribbed from apr/threadproc/win32/proc.c, a
+ // function (unfortunately static) called why_from_exit_code():
+ /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
+ * this class of failures was determined
+ */
+ if ((status & 0xFFFF0000) == 0xC0000000)
+ {
+ result.mState = LLProcess::KILLED;
+ }
+ else
+ {
+ result.mState = LLProcess::EXITED;
+ }
+ result.mData = status;
+
+ return result;
+}
+
+/// GetLastError()/FormatMessage() boilerplate
+static std::string WindowsErrorString(const std::string& operation)
+{
+ int result = GetLastError();
+
+ LPTSTR error_str = 0;
+ if (FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ result,
+ 0,
+ (LPTSTR)&error_str,
+ 0,
+ NULL)
+ != 0)
+ {
+ // convert from wide-char string to multi-byte string
+ char message[256];
+ wcstombs(message, error_str, sizeof(message));
+ message[sizeof(message)-1] = 0;
+ LocalFree(error_str);
+ // convert to std::string to trim trailing whitespace
+ std::string mbsstr(message);
+ mbsstr.erase(mbsstr.find_last_not_of(" \t\r\n"));
+ return STRINGIZE(operation << " failed (" << result << "): " << mbsstr);
+ }
+ return STRINGIZE(operation << " failed (" << result
+ << "), but FormatMessage() did not explain");
+}
+
+/*****************************************************************************
+* Posix specific
+*****************************************************************************/
+#else // Mac and linux
+
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+void LLProcess::autokill()
+{
+ // What we ought to do here is to:
+ // 1. create a unique process group and run all autokill children in that
+ // group (see https://jira.secondlife.com/browse/SWAT-563);
+ // 2. figure out a way to intercept control when the viewer exits --
+ // gracefully or not;
+ // 3. when the viewer exits, kill off the aforementioned process group.
+
+ // It's point 2 that's troublesome. Although I've seen some signal-
+ // handling logic in the Posix viewer code, I haven't yet found any bit of
+ // code that's run no matter how the viewer exits (a try/finally for the
+ // whole process, as it were).
+}
+
+// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
+static bool reap_pid(pid_t pid, LLProcess::Status* pstatus=NULL)
+{
+ LLProcess::Status dummy;
+ if (! pstatus)
+ {
+ // If caller doesn't want to see Status, give us a target anyway so we
+ // don't have to have a bunch of conditionals.
+ pstatus = &dummy;
+ }
+
+ int status = 0;
+ pid_t wait_result = ::waitpid(pid, &status, WNOHANG);
+ if (wait_result == pid)
+ {
+ *pstatus = interpret_status(status);
+ return true;
+ }
+ if (wait_result == 0)
+ {
+ pstatus->mState = LLProcess::RUNNING;
+ pstatus->mData = 0;
+ return false;
+ }
+
+ // Clear caller's Status block; caller must interpret UNSTARTED to mean
+ // "if this PID was ever valid, it no longer is."
+ *pstatus = LLProcess::Status();
+
+ // We've dealt with the success cases: we were able to reap the child
+ // (wait_result == pid) or it's still running (wait_result == 0). It may
+ // be that the child terminated but didn't hang around long enough for us
+ // to reap. In that case we still have no Status to report, but we can at
+ // least state that it's not running.
+ if (wait_result == -1 && errno == ECHILD)
+ {
+ // No such process -- this may mean we're ignoring SIGCHILD.
+ return true;
+ }
+
+ // Uh, should never happen?!
+ LL_WARNS("LLProcess") << "LLProcess::reap_pid(): waitpid(" << pid << ") returned "
+ << wait_result << "; not meaningful?" << LL_ENDL;
+ // If caller is looping until this pid terminates, and if we can't find
+ // out, better to break the loop than to claim it's still running.
+ return true;
+}
+
+LLProcess::id LLProcess::isRunning(id pid, const std::string& desc)
+{
+ // This direct Posix implementation is because we have no access to the
+ // apr_proc_t struct: we expect it's been destroyed.
+ if (! pid)
+ return 0;
+
+ // Check whether the process has exited, and reap it if it has.
+ LLProcess::Status status;
+ if(reap_pid(pid, &status))
+ {
+ // the process has exited.
+ if (! desc.empty())
+ {
+ std::string statstr(desc + " apparently terminated: no status available");
+ // We don't just pass UNSTARTED to getStatusString() because, in
+ // the context of reap_pid(), that state has special meaning.
+ if (status.mState != UNSTARTED)
+ {
+ statstr = getStatusString(desc, status);
+ }
+ LL_INFOS("LLProcess") << statstr << LL_ENDL;
+ }
+ return 0;
+ }
+
+ return pid;
+}
+
+static LLProcess::Status interpret_status(int status)
+{
+ LLProcess::Status result;
+
+ if (WIFEXITED(status))
+ {
+ result.mState = LLProcess::EXITED;
+ result.mData = WEXITSTATUS(status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ result.mState = LLProcess::KILLED;
+ result.mData = WTERMSIG(status);
+ }
+ else // uh, shouldn't happen?
+ {
+ result.mState = LLProcess::EXITED;
+ result.mData = status; // someone else will have to decode
+ }
+
+ return result;
+}
+
+#endif // Posix
diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h
new file mode 100755
index 0000000000..43ccadc412
--- /dev/null
+++ b/indra/llcommon/llprocess.h
@@ -0,0 +1,553 @@
+/**
+ * @file llprocess.h
+ * @brief Utility class for launching, terminating, and tracking child processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPROCESS_H
+#define LL_LLPROCESS_H
+
+#include "llinitparam.h"
+#include "llsdparam.h"
+#include "llwin32headerslean.h"
+#include "apr_thread_proc.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/ptr_container/ptr_vector.hpp>
+#include <boost/optional.hpp>
+#include <boost/noncopyable.hpp>
+#include <iosfwd> // std::ostream
+#include <stdexcept>
+
+#if LL_WINDOWS
+#include "llwin32headerslean.h" // for HANDLE
+#elif LL_LINUX
+#if defined(Status)
+#undef Status
+#endif
+#endif
+
+class LLEventPump;
+
+class LLProcess;
+/// LLProcess instances are created on the heap by static factory methods and
+/// managed by ref-counted pointers.
+typedef boost::shared_ptr<LLProcess> LLProcessPtr;
+
+/**
+ * LLProcess handles launching an external process with specified command line
+ * arguments. It also keeps track of whether the process is still running, and
+ * can kill it if required.
+ *
+ * In discussing LLProcess, we use the term "parent" to refer to this process
+ * (the process invoking LLProcess), versus "child" to refer to the process
+ * spawned by LLProcess.
+ *
+ * LLProcess relies on periodic post() calls on the "mainloop" LLEventPump: an
+ * LLProcess object's Status won't update until the next "mainloop" tick. For
+ * instance, the Second Life viewer's main loop already posts to an
+ * LLEventPump by that name once per iteration. See
+ * indra/llcommon/tests/llprocess_test.cpp for an example of waiting for
+ * child-process termination in a standalone test context.
+ */
+class LL_COMMON_API LLProcess: public boost::noncopyable
+{
+ LOG_CLASS(LLProcess);
+public:
+ /**
+ * Specify what to pass for each of child stdin, stdout, stderr.
+ * @see LLProcess::Params::files.
+ */
+ struct FileParam: public LLInitParam::Block<FileParam>
+ {
+ /**
+ * type of file handle to pass to child process
+ *
+ * - "" (default): let the child inherit the same file handle used by
+ * this process. For instance, if passed as stdout, child stdout
+ * will be interleaved with stdout from this process. In this case,
+ * @a name is moot and should be left "".
+ *
+ * - "file": open an OS filesystem file with the specified @a name.
+ * <i>Not yet implemented.</i>
+ *
+ * - "pipe" or "tpipe" or "npipe": depends on @a name
+ *
+ * - @a name.empty(): construct an OS pipe used only for this slot
+ * of the forthcoming child process.
+ *
+ * - ! @a name.empty(): in a global registry, find or create (using
+ * the specified @a name) an OS pipe. The point of the (purely
+ * internal) @a name is that passing the same @a name in more than
+ * one slot for a given LLProcess -- or for slots in different
+ * LLProcess instances -- means the same pipe. For example, you
+ * might pass the same @a name value as both stdout and stderr to
+ * make the child process produce both on the same actual pipe. Or
+ * you might pass the same @a name as the stdout for one LLProcess
+ * and the stdin for another to connect the two child processes.
+ * Use LLProcess::getPipeName() to generate a unique name
+ * guaranteed not to already exist in the registry. <i>Not yet
+ * implemented.</i>
+ *
+ * The difference between "pipe", "tpipe" and "npipe" is as follows.
+ *
+ * - "pipe": direct LLProcess to monitor the parent end of the pipe,
+ * pumping nonblocking I/O every frame. The expectation (at least
+ * for stdout or stderr) is that the caller will listen for
+ * incoming data and consume it as it arrives. It's important not
+ * to neglect such a pipe, because it's buffered in memory. If you
+ * suspect the child may produce a great volume of output between
+ * frames, consider directing the child to write to a filesystem
+ * file instead, then read the file later.
+ *
+ * - "tpipe": do not engage LLProcess machinery to monitor the
+ * parent end of the pipe. A "tpipe" is used only to connect
+ * different child processes. As such, it makes little sense to
+ * pass an empty @a name. <i>Not yet implemented.</i>
+ *
+ * - "npipe": like "tpipe", but use an OS named pipe with a
+ * generated name. Note that @a name is the @em internal name of
+ * the pipe in our global registry -- it doesn't necessarily have
+ * anything to do with the pipe's name in the OS filesystem. Use
+ * LLProcess::getPipeName() to obtain the named pipe's OS
+ * filesystem name, e.g. to pass it as the @a name to another
+ * LLProcess instance using @a type "file". This supports usage
+ * like bash's &lt;(subcommand...) or &gt;(subcommand...)
+ * constructs. <i>Not yet implemented.</i>
+ *
+ * In all cases the open mode (read, write) is determined by the child
+ * slot you're filling. Child stdin means select the "read" end of a
+ * pipe, or open a filesystem file for reading; child stdout or stderr
+ * means select the "write" end of a pipe, or open a filesystem file
+ * for writing.
+ *
+ * Confusion such as passing the same pipe as the stdin of two
+ * processes (rather than stdout for one and stdin for the other) is
+ * explicitly permitted: it's up to the caller to construct meaningful
+ * LLProcess pipe graphs.
+ */
+ Optional<std::string> type;
+ Optional<std::string> name;
+
+ FileParam(const std::string& tp="", const std::string& nm=""):
+ type("type"),
+ name("name")
+ {
+ // If caller wants to specify values, use explicit assignment to
+ // set them rather than initialization.
+ if (! tp.empty()) type = tp;
+ if (! nm.empty()) name = nm;
+ }
+ };
+
+ /// Param block definition
+ struct Params: public LLInitParam::Block<Params>
+ {
+ Params():
+ executable("executable"),
+ args("args"),
+ cwd("cwd"),
+ autokill("autokill", true),
+ files("files"),
+ postend("postend"),
+ desc("desc")
+ {}
+
+ /// pathname of executable
+ Mandatory<std::string> executable;
+ /**
+ * zero or more additional command-line arguments. Arguments are
+ * passed through as exactly as we can manage, whitespace and all.
+ * @note On Windows we manage this by implicitly double-quoting each
+ * argument while assembling the command line.
+ */
+ Multiple<std::string> args;
+ /// current working directory, if need it changed
+ Optional<std::string> cwd;
+ /// implicitly kill process on destruction of LLProcess object
+ /// (default true)
+ Optional<bool> autokill;
+ /**
+ * Up to three FileParam items: for child stdin, stdout, stderr.
+ * Passing two FileParam entries means default treatment for stderr,
+ * and so forth.
+ *
+ * @note LLInitParam::Block permits usage like this:
+ * @code
+ * LLProcess::Params params;
+ * ...
+ * params.files
+ * .add(LLProcess::FileParam()) // stdin
+ * .add(LLProcess::FileParam().type("pipe") // stdout
+ * .add(LLProcess::FileParam().type("file").name("error.log"));
+ * @endcode
+ *
+ * @note While it's theoretically plausible to pass additional open
+ * file handles to a child specifically written to expect them, our
+ * underlying implementation doesn't yet support that.
+ */
+ Multiple<FileParam, AtMost<3> > files;
+ /**
+ * On child-process termination, if this LLProcess object still
+ * exists, post LLSD event to LLEventPump with specified name (default
+ * no event). Event contains at least:
+ *
+ * - "id" as obtained from getProcessID()
+ * - "desc" short string description of child (executable + pid)
+ * - "state" @c state enum value, from Status.mState
+ * - "data" if "state" is EXITED, exit code; if KILLED, on Posix,
+ * signal number
+ * - "string" English text describing "state" and "data" (e.g. "exited
+ * with code 0")
+ */
+ Optional<std::string> postend;
+ /**
+ * Description of child process for logging purposes. It need not be
+ * unique; the logged description string will contain the PID as well.
+ * If this is omitted, a description will be derived from the
+ * executable name.
+ */
+ Optional<std::string> desc;
+ };
+ typedef LLSDParamAdapter<Params> LLSDOrParams;
+
+ /**
+ * Factory accepting either plain LLSD::Map or Params block.
+ * MAY RETURN DEFAULT-CONSTRUCTED LLProcessPtr if params invalid!
+ */
+ static LLProcessPtr create(const LLSDOrParams& params);
+ virtual ~LLProcess();
+
+ /// Is child process still running?
+ bool isRunning() const;
+ // static isRunning(LLProcessPtr), getStatus(LLProcessPtr),
+ // getStatusString(LLProcessPtr), kill(LLProcessPtr) handle the case in
+ // which the passed LLProcessPtr might be NULL (default-constructed).
+ static bool isRunning(const LLProcessPtr&);
+
+ /**
+ * State of child process
+ */
+ enum state
+ {
+ UNSTARTED, ///< initial value, invisible to consumer
+ RUNNING, ///< child process launched
+ EXITED, ///< child process terminated voluntarily
+ KILLED ///< child process terminated involuntarily
+ };
+
+ /**
+ * Status info
+ */
+ struct Status
+ {
+ Status():
+ mState(UNSTARTED),
+ mData(0)
+ {}
+
+ state mState; ///< @see state
+ /**
+ * - for mState == EXITED: mData is exit() code
+ * - for mState == KILLED: mData is signal number (Posix)
+ * - otherwise: mData is undefined
+ */
+ int mData;
+ };
+
+ /// Status query
+ Status getStatus() const;
+ static Status getStatus(const LLProcessPtr&);
+ /// English Status string query, for logging etc.
+ std::string getStatusString() const;
+ static std::string getStatusString(const std::string& desc, const LLProcessPtr&);
+ /// English Status string query for previously-captured Status
+ std::string getStatusString(const Status& status) const;
+ /// static English Status string query
+ static std::string getStatusString(const std::string& desc, const Status& status);
+
+ // Attempt to kill the process -- returns true if the process is no longer running when it returns.
+ // Note that even if this returns false, the process may exit some time after it's called.
+ bool kill(const std::string& who="");
+ static bool kill(const LLProcessPtr& p, const std::string& who="");
+
+#if LL_WINDOWS
+ typedef int id; ///< as returned by getProcessID()
+ typedef HANDLE handle; ///< as returned by getProcessHandle()
+#else
+ typedef pid_t id;
+ typedef pid_t handle;
+#endif
+ /**
+ * Get an int-like id value. This is primarily intended for a human reader
+ * to differentiate processes.
+ */
+ id getProcessID() const;
+ /**
+ * Get a "handle" of a kind that you might pass to platform-specific API
+ * functions to engage features not directly supported by LLProcess.
+ */
+ handle getProcessHandle() const;
+
+ /**
+ * Test if a process (@c handle obtained from getProcessHandle()) is still
+ * running. Return same nonzero @c handle value if still running, else
+ * zero, so you can test it like a bool. But if you want to update a
+ * stored variable as a side effect, you can write code like this:
+ * @code
+ * hchild = LLProcess::isRunning(hchild);
+ * @endcode
+ * @note This method is intended as a unit-test hook, not as the first of
+ * a whole set of operations supported on freestanding @c handle values.
+ * New functionality should be added as nonstatic members operating on
+ * the same data as getProcessHandle().
+ *
+ * In particular, if child termination is detected by this static isRunning()
+ * rather than by nonstatic isRunning(), the LLProcess object won't be
+ * aware of the child's changed status and may encounter OS errors trying
+ * to obtain it. This static isRunning() is only intended for after the
+ * launching LLProcess object has been destroyed.
+ */
+ static handle isRunning(handle, const std::string& desc="");
+
+ /// Provide symbolic access to child's file slots
+ enum FILESLOT { STDIN=0, STDOUT=1, STDERR=2, NSLOTS=3 };
+
+ /**
+ * For a pipe constructed with @a type "npipe", obtain the generated OS
+ * filesystem name for the specified pipe. Otherwise returns the empty
+ * string. @see LLProcess::FileParam::type
+ */
+ std::string getPipeName(FILESLOT) const;
+
+ /// base of ReadPipe, WritePipe
+ class LL_COMMON_API BasePipe
+ {
+ public:
+ virtual ~BasePipe() = 0;
+
+ typedef std::size_t size_type;
+ static const size_type npos;
+
+ /**
+ * Get accumulated buffer length.
+ *
+ * For WritePipe, is there still pending data to send to child?
+ *
+ * For ReadPipe, we often need to refrain from actually reading the
+ * std::istream returned by get_istream() until we've accumulated
+ * enough data to make it worthwhile. For instance, if we're expecting
+ * a number from the child, but the child happens to flush "12" before
+ * emitting "3\n", get_istream() >> myint could return 12 rather than
+ * 123!
+ */
+ virtual size_type size() const = 0;
+ };
+
+ /// As returned by getWritePipe() or getOptWritePipe()
+ class WritePipe: public BasePipe
+ {
+ public:
+ /**
+ * Get ostream& on which to write to child's stdin.
+ *
+ * @usage
+ * @code
+ * myProcess->getWritePipe().get_ostream() << "Hello, child!" << std::endl;
+ * @endcode
+ */
+ virtual std::ostream& get_ostream() = 0;
+ };
+
+ /// As returned by getReadPipe() or getOptReadPipe()
+ class ReadPipe: public BasePipe
+ {
+ public:
+ /**
+ * Get istream& on which to read from child's stdout or stderr.
+ *
+ * @usage
+ * @code
+ * std::string stuff;
+ * myProcess->getReadPipe().get_istream() >> stuff;
+ * @endcode
+ *
+ * You should be sure in advance that the ReadPipe in question can
+ * fill the request. @see getPump()
+ */
+ virtual std::istream& get_istream() = 0;
+
+ /**
+ * Like std::getline(get_istream(), line), but trims off trailing '\r'
+ * to make calling code less platform-sensitive.
+ */
+ virtual std::string getline() = 0;
+
+ /**
+ * Like get_istream().read(buffer, n), but returns std::string rather
+ * than requiring caller to construct a buffer, etc.
+ */
+ virtual std::string read(size_type len) = 0;
+
+ /**
+ * Peek at accumulated buffer data without consuming it. Optional
+ * parameters give you substr() functionality.
+ *
+ * @note You can discard buffer data using get_istream().ignore(n).
+ */
+ virtual std::string peek(size_type offset=0, size_type len=npos) const = 0;
+
+ /**
+ * Detect presence of a substring (or char) in accumulated buffer data
+ * without retrieving it. Optional offset allows you to search from
+ * specified position.
+ */
+ template <typename SEEK>
+ bool contains(SEEK seek, size_type offset=0) const
+ { return find(seek, offset) != npos; }
+
+ /**
+ * Search for a substring in accumulated buffer data without
+ * retrieving it. Returns size_type position at which found, or npos
+ * meaning not found. Optional offset allows you to search from
+ * specified position.
+ */
+ virtual size_type find(const std::string& seek, size_type offset=0) const = 0;
+
+ /**
+ * Search for a char in accumulated buffer data without retrieving it.
+ * Returns size_type position at which found, or npos meaning not
+ * found. Optional offset allows you to search from specified
+ * position.
+ */
+ virtual size_type find(char seek, size_type offset=0) const = 0;
+
+ /**
+ * Get LLEventPump& on which to listen for incoming data. The posted
+ * LLSD::Map event will contain:
+ *
+ * - "data" part of pending data; see setLimit()
+ * - "len" entire length of pending data, regardless of setLimit()
+ * - "slot" this ReadPipe's FILESLOT, e.g. LLProcess::STDOUT
+ * - "name" e.g. "stdout"
+ * - "desc" e.g. "SLPlugin (pid) stdout"
+ * - "eof" @c true means there no more data will arrive on this pipe,
+ * therefore no more events on this pump
+ *
+ * If the child sends "abc", and this ReadPipe posts "data"="abc", but
+ * you don't consume it by reading the std::istream returned by
+ * get_istream(), and the child next sends "def", ReadPipe will post
+ * "data"="abcdef".
+ */
+ virtual LLEventPump& getPump() = 0;
+
+ /**
+ * Set maximum length of buffer data that will be posted in the LLSD
+ * announcing arrival of new data from the child. If you call
+ * setLimit(5), and the child sends "abcdef", the LLSD event will
+ * contain "data"="abcde". However, you may still read the entire
+ * "abcdef" from get_istream(): this limit affects only the size of
+ * the data posted with the LLSD event. If you don't call this method,
+ * @em no data will be posted: the default is 0 bytes.
+ */
+ virtual void setLimit(size_type limit) = 0;
+
+ /**
+ * Query the current setLimit() limit.
+ */
+ virtual size_type getLimit() const = 0;
+ };
+
+ /// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to
+ /// create a pipe at the corresponding FILESLOT.
+ struct NoPipe: public std::runtime_error
+ {
+ NoPipe(const std::string& what): std::runtime_error(what) {}
+ };
+
+ /**
+ * Get a reference to the (only) WritePipe for this LLProcess. @a slot, if
+ * specified, must be STDIN. Throws NoPipe if you did not request a "pipe"
+ * for child stdin. Use this method when you know how you created the
+ * LLProcess in hand.
+ */
+ WritePipe& getWritePipe(FILESLOT slot=STDIN);
+
+ /**
+ * Get a boost::optional<WritePipe&> to the (only) WritePipe for this
+ * LLProcess. @a slot, if specified, must be STDIN. The return value is
+ * empty if you did not request a "pipe" for child stdin. Use this method
+ * for inspecting an LLProcess you did not create.
+ */
+ boost::optional<WritePipe&> getOptWritePipe(FILESLOT slot=STDIN);
+
+ /**
+ * Get a reference to one of the ReadPipes for this LLProcess. @a slot, if
+ * specified, must be STDOUT or STDERR. Throws NoPipe if you did not
+ * request a "pipe" for child stdout or stderr. Use this method when you
+ * know how you created the LLProcess in hand.
+ */
+ ReadPipe& getReadPipe(FILESLOT slot);
+
+ /**
+ * Get a boost::optional<ReadPipe&> to one of the ReadPipes for this
+ * LLProcess. @a slot, if specified, must be STDOUT or STDERR. The return
+ * value is empty if you did not request a "pipe" for child stdout or
+ * stderr. Use this method for inspecting an LLProcess you did not create.
+ */
+ boost::optional<ReadPipe&> getOptReadPipe(FILESLOT slot);
+
+ /// little utilities that really should already be somewhere else in the
+ /// code base
+ static std::string basename(const std::string& path);
+ static std::string getline(std::istream&);
+
+private:
+ /// constructor is private: use create() instead
+ LLProcess(const LLSDOrParams& params);
+ void autokill();
+ // Classic-C-style APR callback
+ static void status_callback(int reason, void* data, int status);
+ // Object-oriented callback
+ void handle_status(int reason, int status);
+ // implementation for get[Opt][Read|Write]Pipe()
+ template <class PIPETYPE>
+ PIPETYPE& getPipe(FILESLOT slot);
+ template <class PIPETYPE>
+ boost::optional<PIPETYPE&> getOptPipe(FILESLOT slot);
+ template <class PIPETYPE>
+ PIPETYPE* getPipePtr(std::string& error, FILESLOT slot);
+
+ std::string mDesc;
+ std::string mPostend;
+ apr_proc_t mProcess;
+ bool mAutokill;
+ Status mStatus;
+ // explicitly want this ptr_vector to be able to store NULLs
+ typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector;
+ PipeVector mPipes;
+};
+
+/// for logging
+LL_COMMON_API std::ostream& operator<<(std::ostream&, const LLProcess::Params&);
+
+#endif // LL_LLPROCESS_H
diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp
deleted file mode 100644
index f0315e92eb..0000000000
--- a/indra/llcommon/llprocesslauncher.cpp
+++ /dev/null
@@ -1,344 +0,0 @@
-/**
- * @file llprocesslauncher.cpp
- * @brief Utility class for launching, terminating, and tracking the state of processes.
- *
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llprocesslauncher.h"
-
-#include <iostream>
-#if LL_DARWIN || LL_LINUX
-// not required or present on Win32
-#include <sys/wait.h>
-#endif
-
-LLProcessLauncher::LLProcessLauncher()
-{
-#if LL_WINDOWS
- mProcessHandle = 0;
-#else
- mProcessID = 0;
-#endif
-}
-
-LLProcessLauncher::~LLProcessLauncher()
-{
- kill();
-}
-
-void LLProcessLauncher::setExecutable(const std::string &executable)
-{
- mExecutable = executable;
-}
-
-void LLProcessLauncher::setWorkingDirectory(const std::string &dir)
-{
- mWorkingDir = dir;
-}
-
-void LLProcessLauncher::clearArguments()
-{
- mLaunchArguments.clear();
-}
-
-void LLProcessLauncher::addArgument(const std::string &arg)
-{
- mLaunchArguments.push_back(arg);
-}
-
-void LLProcessLauncher::addArgument(const char *arg)
-{
- mLaunchArguments.push_back(std::string(arg));
-}
-
-#if LL_WINDOWS
-
-int LLProcessLauncher::launch(void)
-{
- // If there was already a process associated with this object, kill it.
- kill();
- orphan();
-
- int result = 0;
-
- PROCESS_INFORMATION pinfo;
- STARTUPINFOA sinfo;
- memset(&sinfo, 0, sizeof(sinfo));
-
- std::string args = mExecutable;
- for(int i = 0; i < (int)mLaunchArguments.size(); i++)
- {
- args += " ";
- args += mLaunchArguments[i];
- }
-
- // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string...
- char *args2 = new char[args.size() + 1];
- strcpy(args2, args.c_str());
-
- if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo ) )
- {
- // TODO: do better than returning the OS-specific error code on failure...
- result = GetLastError();
- if(result == 0)
- {
- // Make absolutely certain we return a non-zero value on failure.
- result = -1;
- }
- }
- else
- {
- // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on
- // CloseHandle(pinfo.hProcess); // stops leaks - nothing else
- mProcessHandle = pinfo.hProcess;
- CloseHandle(pinfo.hThread); // stops leaks - nothing else
- }
-
- delete[] args2;
-
- return result;
-}
-
-bool LLProcessLauncher::isRunning(void)
-{
- if(mProcessHandle != 0)
- {
- DWORD waitresult = WaitForSingleObject(mProcessHandle, 0);
- if(waitresult == WAIT_OBJECT_0)
- {
- // the process has completed.
- mProcessHandle = 0;
- }
- }
-
- return (mProcessHandle != 0);
-}
-bool LLProcessLauncher::kill(void)
-{
- bool result = true;
-
- if(mProcessHandle != 0)
- {
- TerminateProcess(mProcessHandle,0);
-
- if(isRunning())
- {
- result = false;
- }
- }
-
- return result;
-}
-
-void LLProcessLauncher::orphan(void)
-{
- // Forget about the process
- mProcessHandle = 0;
-}
-
-// static
-void LLProcessLauncher::reap(void)
-{
- // No actions necessary on Windows.
-}
-
-#else // Mac and linux
-
-#include <signal.h>
-#include <fcntl.h>
-#include <errno.h>
-
-static std::list<pid_t> sZombies;
-
-// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
-static bool reap_pid(pid_t pid)
-{
- bool result = false;
-
- pid_t wait_result = ::waitpid(pid, NULL, WNOHANG);
- if(wait_result == pid)
- {
- result = true;
- }
- else if(wait_result == -1)
- {
- if(errno == ECHILD)
- {
- // No such process -- this may mean we're ignoring SIGCHILD.
- result = true;
- }
- }
-
- return result;
-}
-
-int LLProcessLauncher::launch(void)
-{
- // If there was already a process associated with this object, kill it.
- kill();
- orphan();
-
- int result = 0;
- int current_wd = -1;
-
- // create an argv vector for the child process
- const char ** fake_argv = new const char *[mLaunchArguments.size() + 2]; // 1 for the executable path, 1 for the NULL terminator
-
- int i = 0;
-
- // add the executable path
- fake_argv[i++] = mExecutable.c_str();
-
- // and any arguments
- for(int j=0; j < mLaunchArguments.size(); j++)
- fake_argv[i++] = mLaunchArguments[j].c_str();
-
- // terminate with a null pointer
- fake_argv[i] = NULL;
-
- if(!mWorkingDir.empty())
- {
- // save the current working directory
- current_wd = ::open(".", O_RDONLY);
-
- // and change to the one the child will be executed in
- if (::chdir(mWorkingDir.c_str()))
- {
- // chdir failed
- }
- }
-
- // flush all buffers before the child inherits them
- ::fflush(NULL);
-
- pid_t id = vfork();
- if(id == 0)
- {
- // child process
-
- ::execv(mExecutable.c_str(), (char * const *)fake_argv);
-
- // If we reach this point, the exec failed.
- // Use _exit() instead of exit() per the vfork man page.
- _exit(0);
- }
-
- // parent process
-
- if(current_wd >= 0)
- {
- // restore the previous working directory
- if (::fchdir(current_wd))
- {
- // chdir failed
- }
- ::close(current_wd);
- }
-
- delete[] fake_argv;
-
- mProcessID = id;
-
- // At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked)
- // If the process doesn't exist at this point, the exec failed.
- if(!isRunning())
- {
- result = -1;
- }
-
- return result;
-}
-
-bool LLProcessLauncher::isRunning(void)
-{
- if(mProcessID != 0)
- {
- // Check whether the process has exited, and reap it if it has.
- if(reap_pid(mProcessID))
- {
- // the process has exited.
- mProcessID = 0;
- }
- }
-
- return (mProcessID != 0);
-}
-
-bool LLProcessLauncher::kill(void)
-{
- bool result = true;
-
- if(mProcessID != 0)
- {
- // Try to kill the process. We'll do approximately the same thing whether the kill returns an error or not, so we ignore the result.
- (void)::kill(mProcessID, SIGTERM);
-
- // This will have the side-effect of reaping the zombie if the process has exited.
- if(isRunning())
- {
- result = false;
- }
- }
-
- return result;
-}
-
-void LLProcessLauncher::orphan(void)
-{
- // Disassociate the process from this object
- if(mProcessID != 0)
- {
- // We may still need to reap the process's zombie eventually
- sZombies.push_back(mProcessID);
-
- mProcessID = 0;
- }
-}
-
-// static
-void LLProcessLauncher::reap(void)
-{
- // Attempt to real all saved process ID's.
-
- std::list<pid_t>::iterator iter = sZombies.begin();
- while(iter != sZombies.end())
- {
- if(reap_pid(*iter))
- {
- iter = sZombies.erase(iter);
- }
- else
- {
- iter++;
- }
- }
-}
-
-#endif
diff --git a/indra/llcommon/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h
deleted file mode 100644
index 929d547f6e..0000000000
--- a/indra/llcommon/llprocesslauncher.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * @file llprocesslauncher.h
- * @brief Utility class for launching, terminating, and tracking the state of processes.
- *
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlife.com/developers/opensource/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at http://secondlife.com/developers/opensource/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPROCESSLAUNCHER_H
-#define LL_LLPROCESSLAUNCHER_H
-
-#if LL_WINDOWS
-#include <windows.h>
-#endif
-
-
-/*
- LLProcessLauncher handles launching external processes with specified command line arguments.
- It also keeps track of whether the process is still running, and can kill it if required.
-*/
-
-class LL_COMMON_API LLProcessLauncher
-{
- LOG_CLASS(LLProcessLauncher);
-public:
- LLProcessLauncher();
- virtual ~LLProcessLauncher();
-
- void setExecutable(const std::string &executable);
- void setWorkingDirectory(const std::string &dir);
-
- void clearArguments();
- void addArgument(const std::string &arg);
- void addArgument(const char *arg);
-
- int launch(void);
- bool isRunning(void);
-
- // Attempt to kill the process -- returns true if the process is no longer running when it returns.
- // Note that even if this returns false, the process may exit some time after it's called.
- bool kill(void);
-
- // Use this if you want the external process to continue execution after the LLProcessLauncher instance controlling it is deleted.
- // Normally, the destructor will attempt to kill the process and wait for termination.
- // This should only be used if the viewer is about to exit -- otherwise, the child process will become a zombie after it exits.
- void orphan(void);
-
- // This needs to be called periodically on Mac/Linux to clean up zombie processes.
- static void reap(void);
-
- // Accessors for platform-specific process ID
-#if LL_WINDOWS
- HANDLE getProcessHandle() { return mProcessHandle; };
-#else
- pid_t getProcessID() { return mProcessID; };
-#endif
-
-private:
- std::string mExecutable;
- std::string mWorkingDir;
- std::vector<std::string> mLaunchArguments;
-
-#if LL_WINDOWS
- HANDLE mProcessHandle;
-#else
- pid_t mProcessID;
-#endif
-};
-
-#endif // LL_LLPROCESSLAUNCHER_H
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
index 8a4a4a8f9a..e3e1d0c391 100644..100755
--- a/indra/llcommon/llprocessor.cpp
+++ b/indra/llcommon/llprocessor.cpp
@@ -2,158 +2,394 @@
* @file llprocessor.cpp
* @brief Code to figure out the processor. Originally by Benjamin Jurke.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-// 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 "llprocessor.h"
-#include "processor.h"
+#include "llerror.h"
-#include <memory>
+//#include <memory>
#if LL_WINDOWS
-# define WIN32_LEAN_AND_MEAN
-# include <winsock2.h>
-# include <windows.h>
+# include "llwin32headerslean.h"
+# define _interlockedbittestandset _renamed_interlockedbittestandset
+# define _interlockedbittestandreset _renamed_interlockedbittestandreset
+# include <intrin.h>
+# undef _interlockedbittestandset
+# undef _interlockedbittestandreset
#endif
-#if !LL_DARWIN && !LL_SOLARIS
-
-#ifdef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
-// We need the QueryPerformanceCounter and Sleep functions
-#define FORCEINLINE __forceinline
-#else
-#define FORCEINLINE
+#include "llsd.h"
+
+#if LL_MSVC && _M_X64
+# define LL_X86_64 1
+# define LL_X86 1
+#elif LL_MSVC && _M_IX86
+# define LL_X86 1
+#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
+# define LL_X86_64 1
+# define LL_X86 1
+#elif LL_GNUC && ( defined(__i386__) )
+# define LL_X86 1
+#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) )
+# define LL_PPC 1
#endif
+class LLProcessorInfoImpl; // foward declaration for the mImpl;
+
+namespace
+{
+ enum cpu_info
+ {
+ eBrandName = 0,
+ eFrequency,
+ eVendor,
+ eStepping,
+ eFamily,
+ eExtendedFamily,
+ eModel,
+ eExtendedModel,
+ eType,
+ eBrandID,
+ eFamilyName
+ };
+
+
+ const char* cpu_info_names[] =
+ {
+ "Processor Name",
+ "Frequency",
+ "Vendor",
+ "Stepping",
+ "Family",
+ "Extended Family",
+ "Model",
+ "Extended Model",
+ "Type",
+ "Brand ID",
+ "Family Name"
+ };
+
+ enum cpu_config
+ {
+ eMaxID,
+ eMaxExtID,
+ eCLFLUSHCacheLineSize,
+ eAPICPhysicalID,
+ eCacheLineSize,
+ eL2Associativity,
+ eCacheSizeK,
+ eFeatureBits,
+ eExtFeatureBits
+ };
+
+ const char* cpu_config_names[] =
+ {
+ "Max Supported CPUID level",
+ "Max Supported Ext. CPUID level",
+ "CLFLUSH cache line size",
+ "APIC Physical ID",
+ "Cache Line Size",
+ "L2 Associativity",
+ "Cache Size",
+ "Feature Bits",
+ "Ext. Feature Bits"
+ };
+
+
+
+ // *NOTE:Mani - this contains the elements we reference directly and extensions beyond the first 32.
+ // The rest of the names are referenced by bit maks returned from cpuid.
+ enum cpu_features
+ {
+ eSSE_Ext=25,
+ eSSE2_Ext=26,
+
+ eSSE3_Features=32,
+ eMONTIOR_MWAIT=33,
+ eCPLDebugStore=34,
+ eThermalMonitor2=35,
+ eAltivec=36
+ };
+
+ const char* cpu_feature_names[] =
+ {
+ "x87 FPU On Chip",
+ "Virtual-8086 Mode Enhancement",
+ "Debugging Extensions",
+ "Page Size Extensions",
+ "Time Stamp Counter",
+ "RDMSR and WRMSR Support",
+ "Physical Address Extensions",
+ "Machine Check Exception",
+ "CMPXCHG8B Instruction",
+ "APIC On Chip",
+ "Unknown1",
+ "SYSENTER and SYSEXIT",
+ "Memory Type Range Registers",
+ "PTE Global Bit",
+ "Machine Check Architecture",
+ "Conditional Move/Compare Instruction",
+ "Page Attribute Table",
+ "Page Size Extension",
+ "Processor Serial Number",
+ "CFLUSH Extension",
+ "Unknown2",
+ "Debug Store",
+ "Thermal Monitor and Clock Ctrl",
+ "MMX Technology",
+ "FXSAVE/FXRSTOR",
+ "SSE Extensions",
+ "SSE2 Extensions",
+ "Self Snoop",
+ "Hyper-threading Technology",
+ "Thermal Monitor",
+ "Unknown4",
+ "Pend. Brk. EN.", // 31 End of FeatureInfo bits
+
+ "SSE3 New Instructions", // 32
+ "MONITOR/MWAIT",
+ "CPL Qualified Debug Store",
+ "Thermal Monitor 2",
+
+ "Altivec"
+ };
+
+ std::string intel_CPUFamilyName(int composed_family)
+ {
+ switch(composed_family)
+ {
+ case 3: return "Intel i386";
+ case 4: return "Intel i486";
+ case 5: return "Intel Pentium";
+ case 6: return "Intel Pentium Pro/2/3, Core";
+ case 7: return "Intel Itanium (IA-64)";
+ case 0xF: return "Intel Pentium 4";
+ case 0x10: return "Intel Itanium 2 (IA-64)";
+ }
+ return "Unknown";
+ }
+
+ std::string amd_CPUFamilyName(int composed_family)
+ {
+ switch(composed_family)
+ {
+ case 4: return "AMD 80486/5x86";
+ case 5: return "AMD K5/K6";
+ case 6: return "AMD K7";
+ case 0xF: return "AMD K8";
+ case 0x10: return "AMD K8L";
+ }
+ return "Unknown";
+ }
+
+ std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family)
+ {
+ const char* intel_string = "GenuineIntel";
+ const char* amd_string = "AuthenticAMD";
+ if(!strncmp(cpu_vendor, intel_string, strlen(intel_string)))
+ {
+ U32 composed_family = family + ext_family;
+ return intel_CPUFamilyName(composed_family);
+ }
+ else if(!strncmp(cpu_vendor, amd_string, strlen(amd_string)))
+ {
+ U32 composed_family = (family == 0xF)
+ ? family + ext_family
+ : family;
+ return amd_CPUFamilyName(composed_family);
+ }
+ return "Unknown";
+ }
+
+} // end unnamed namespace
+
+// The base class for implementations.
+// Each platform should override this class.
+class LLProcessorInfoImpl
+{
+public:
+ LLProcessorInfoImpl()
+ {
+ mProcessorInfo["info"] = LLSD::emptyMap();
+ mProcessorInfo["config"] = LLSD::emptyMap();
+ mProcessorInfo["extension"] = LLSD::emptyMap();
+ }
+ virtual ~LLProcessorInfoImpl() {}
+
+ F64 getCPUFrequency() const
+ {
+ return getInfo(eFrequency, 0).asReal();
+ }
+
+ bool hasSSE() const
+ {
+ return hasExtension(cpu_feature_names[eSSE_Ext]);
+ }
+
+ bool hasSSE2() const
+ {
+ return hasExtension(cpu_feature_names[eSSE2_Ext]);
+ }
+
+ bool hasAltivec() const
+ {
+ return hasExtension("Altivec");
+ }
+
+ std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unknown").asString(); }
+ std::string getCPUBrandName() const { return getInfo(eBrandName, "Unknown").asString(); }
+
+ // This is virtual to support a different linux format.
+ // *NOTE:Mani - I didn't want to screw up server use of this data...
+ virtual std::string getCPUFeatureDescription() const
+ {
+ std::ostringstream out;
+ out << std::endl << std::endl;
+ out << "// CPU General Information" << std::endl;
+ out << "//////////////////////////" << std::endl;
+ out << "Processor Name: " << getCPUBrandName() << std::endl;
+ out << "Frequency: " << getCPUFrequency() << " MHz" << std::endl;
+ out << "Vendor: " << getInfo(eVendor, "Unknown").asString() << std::endl;
+ out << "Family: " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl;
+ out << "Extended family: " << getInfo(eExtendedFamily, 0) << std::endl;
+ out << "Model: " << getInfo(eModel, 0) << std::endl;
+ out << "Extended model: " << getInfo(eExtendedModel, 0) << std::endl;
+ out << "Type: " << getInfo(eType, 0) << std::endl;
+ out << "Brand ID: " << getInfo(eBrandID, 0) << std::endl;
+ out << std::endl;
+ out << "// CPU Configuration" << std::endl;
+ out << "//////////////////////////" << std::endl;
+
+ // Iterate through the dictionary of configuration options.
+ LLSD configs = mProcessorInfo["config"];
+ for(LLSD::map_const_iterator cfgItr = configs.beginMap(); cfgItr != configs.endMap(); ++cfgItr)
+ {
+ out << cfgItr->first << " = " << cfgItr->second << std::endl;
+ }
+ out << std::endl;
+
+ out << "// CPU Extensions" << std::endl;
+ out << "//////////////////////////" << std::endl;
+
+ for(LLSD::map_const_iterator itr = mProcessorInfo["extension"].beginMap(); itr != mProcessorInfo["extension"].endMap(); ++itr)
+ {
+ out << " " << itr->first << std::endl;
+ }
+ return out.str();
+ }
+
+protected:
+ void setInfo(cpu_info info_type, const LLSD& value)
+ {
+ setInfo(cpu_info_names[info_type], value);
+ }
+ LLSD getInfo(cpu_info info_type, const LLSD& defaultVal) const
+ {
+ return getInfo(cpu_info_names[info_type], defaultVal);
+ }
-// Some macros we often need
-////////////////////////////
-#define CheckBit(var, bit) ((var & (1 << bit)) ? true : false)
+ void setConfig(cpu_config config_type, const LLSD& value)
+ {
+ setConfig(cpu_config_names[config_type], value);
+ }
+ LLSD getConfig(cpu_config config_type, const LLSD& defaultVal) const
+ {
+ return getConfig(cpu_config_names[config_type], defaultVal);
+ }
+
+ void setExtension(const std::string& name) { mProcessorInfo["extension"][name] = "true"; }
+ bool hasExtension(const std::string& name) const
+ {
+ return mProcessorInfo["extension"].has(name);
+ }
+
+private:
+ void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; }
+ LLSD getInfo(const std::string& name, const LLSD& defaultVal) const
+ {
+ if(mProcessorInfo["info"].has(name))
+ {
+ return mProcessorInfo["info"][name];
+ }
+ return defaultVal;
+ }
+ void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; }
+ LLSD getConfig(const std::string& name, const LLSD& defaultVal) const
+ {
+ LLSD r = mProcessorInfo["config"].get(name);
+ return r.isDefined() ? r : defaultVal;
+ }
+
+private:
+
+ LLSD mProcessorInfo;
+};
+
+
+#ifdef LL_MSVC
+// LL_MSVC and not LLWINDOWS because some of the following code
+// uses the MSVC compiler intrinsics __cpuid() and __rdtsc().
-#ifdef PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
// Delays for the specified amount of milliseconds
-static void _Delay(unsigned int ms)
+static void _Delay(unsigned int ms)
{
- LARGE_INTEGER freq, c1, c2;
- __int64 x;
+ LARGE_INTEGER freq, c1, c2;
+ __int64 x;
- // Get High-Res Timer frequency
+ // 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
+ // Get first snapshot of High-Res Timer value
QueryPerformanceCounter(&c1);
do
{
- // Get second snapshot
- QueryPerformanceCounter(&c2);
+ // 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;
- strCPUName[0] = 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)
+static F64 calculate_cpu_frequency(U32 measure_msecs)
{
-#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(measure_msecs == 0)
{
- // 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 there"s no high-res timer, we exit.
+ unsigned __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
@@ -169,35 +405,27 @@ F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
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
+ //// Now we call a CPUID to ensure, that all other prior called functions are
+ //// completed now (serialization)
+ //__asm cpuid
+ int cpu_info[4] = {-1};
+ __cpuid(cpu_info, 0);
// 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
- }
+ start = __rdtsc();
// Now we wart for some msecs
- _Delay(uiMeasureMSecs);
-// Sleep(uiMeasureMSecs);
+ _Delay(measure_msecs);
+ // 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
- }
+ end = __rdtsc();
// Now we can restore the default process and thread priorities
SetProcessAffinityMask(hProcess, dwProcessMask);
@@ -210,2075 +438,433 @@ F64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
// And finally the frequency is the clock difference divided by the time
// difference.
- uqwFrequency = (F64)dif / (((F64)timedif) / freq);
+ F64 frequency = (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
+ // member var uqwFrequency - converted to MHz
+ return frequency / (F64)1000000;
}
-// bool CProcessor::AnalyzeIntelProcessor()
-// ========================================
-// Private class function for analyzing an Intel processor
-//////////////////////////////////////////////////////////
-bool CProcessor::AnalyzeIntelProcessor()
+// Windows implementation
+class LLProcessorInfoWindowsImpl : public LLProcessorInfoImpl
{
-#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
+public:
+ LLProcessorInfoWindowsImpl()
{
- mov eax, 1
- cpuid
- mov eaxreg, eax
- mov ebxreg, ebx
- mov edxreg, edx
+ getCPUIDInfo();
+ setInfo(eFrequency, calculate_cpu_frequency(50));
}
-
- // 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;
-
- static const char* INTEL_BRAND[] =
- {
- /* 0x00 */ "",
- /* 0x01 */ "0.18 micron Intel Celeron",
- /* 0x02 */ "0.18 micron Intel Pentium III",
- /* 0x03 */ "0.13 micron Intel Celeron",
- /* 0x04 */ "0.13 micron Intel Pentium III",
- /* 0x05 */ "",
- /* 0x06 */ "0.13 micron Intel Pentium III Mobile",
- /* 0x07 */ "0.13 micron Intel Celeron Mobile",
- /* 0x08 */ "0.18 micron Intel Pentium 4",
- /* 0x09 */ "0.13 micron Intel Pentium 4",
- /* 0x0A */ "0.13 micron Intel Celeron",
- /* 0x0B */ "0.13 micron Intel Pentium 4 Xeon",
- /* 0x0C */ "Intel Xeon MP",
- /* 0x0D */ "",
- /* 0x0E */ "0.18 micron Intel Pentium 4 Xeon",
- /* 0x0F */ "Mobile Intel Celeron",
- /* 0x10 */ "",
- /* 0x11 */ "Mobile Genuine Intel",
- /* 0x12 */ "Intel Celeron M",
- /* 0x13 */ "Mobile Intel Celeron",
- /* 0x14 */ "Intel Celeron",
- /* 0x15 */ "Mobile Genuine Intel",
- /* 0x16 */ "Intel Pentium M",
- /* 0x17 */ "Mobile Intel Celeron",
- };
- // Only override the brand if we have it in the lookup table. We should
- // already have a string here from GetCPUInfo(). JC
- if ( CPUInfo.uiBrandID < LL_ARRAY_SIZE(INTEL_BRAND) )
+private:
+ void getCPUIDInfo()
{
- strncpy(CPUInfo.strBrandID, INTEL_BRAND[CPUInfo.uiBrandID], sizeof(CPUInfo.strBrandID)-1);
- CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0';
+ // http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx
+
+ // __cpuid with an InfoType argument of 0 returns the number of
+ // valid Ids in cpu_info[0] and the CPU identification string in
+ // the other three array elements. The CPU identification string is
+ // not in linear order. The code below arranges the information
+ // in a human readable form.
+ int cpu_info[4] = {-1};
+ __cpuid(cpu_info, 0);
+ unsigned int ids = (unsigned int)cpu_info[0];
+ setConfig(eMaxID, (S32)ids);
- if (CPUInfo.uiBrandID == 3 && CPUInfo.uiModel == 6)
+ char cpu_vendor[0x20];
+ memset(cpu_vendor, 0, sizeof(cpu_vendor));
+ *((int*)cpu_vendor) = cpu_info[1];
+ *((int*)(cpu_vendor+4)) = cpu_info[3];
+ *((int*)(cpu_vendor+8)) = cpu_info[2];
+ setInfo(eVendor, cpu_vendor);
+
+ // Get the information associated with each valid Id
+ for(unsigned int i=0; i<=ids; ++i)
{
- strcpy(CPUInfo.strBrandID, "0.18 micron Intel Pentium III Xeon");
- }
- }
+ __cpuid(cpu_info, i);
- // 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/2/3, Core"); /* 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)
+ // Interpret CPU feature information.
+ if (i == 1)
{
- 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 9: // Model = 9: Intel Pentium M processor, Intel Celeron M processor, model 9
- strcpy(CPUInfo.strModel, "Intel Pentium M Series Processor"); /*Flawfinder: ignore*/
- strncat(strCPUName, "Intel Pentium M Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
- 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)
+ setInfo(eStepping, cpu_info[0] & 0xf);
+ setInfo(eModel, (cpu_info[0] >> 4) & 0xf);
+ int family = (cpu_info[0] >> 8) & 0xf;
+ setInfo(eFamily, family);
+ setInfo(eType, (cpu_info[0] >> 12) & 0x3);
+ setInfo(eExtendedModel, (cpu_info[0] >> 16) & 0xf);
+ int ext_family = (cpu_info[0] >> 20) & 0xff;
+ setInfo(eExtendedFamily, ext_family);
+ setInfo(eBrandID, cpu_info[1] & 0xff);
+
+ setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family));
+
+ setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8);
+ setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff);
+
+ if(cpu_info[2] & 0x1)
+ {
+ setExtension(cpu_feature_names[eSSE3_Features]);
+ }
+
+ if(cpu_info[2] & 0x8)
+ {
+ setExtension(cpu_feature_names[eMONTIOR_MWAIT]);
+ }
+
+ if(cpu_info[2] & 0x10)
+ {
+ setExtension(cpu_feature_names[eCPLDebugStore]);
+ }
+
+ if(cpu_info[2] & 0x100)
+ {
+ setExtension(cpu_feature_names[eThermalMonitor2]);
+ }
+
+ unsigned int feature_info = (unsigned int) cpu_info[3];
+ for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1)
+ {
+ if(feature_info & bit)
{
- 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;
+ setExtension(cpu_feature_names[index]);
}
- 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;
- case 0xD: // Model = 0xD: Intel Pentium M processor, Intel Celeron M processor, model D
- strcpy(CPUInfo.strModel, "Intel Pentium M Series Processor"); /*Flawfinder: ignore*/
- strncat(strCPUName, "Intel Pentium M Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
- break;
- case 0xE: // Model = 0xE: Intel Core Duo processor, Intel Core Solo processor, model E
- strcpy(CPUInfo.strModel, "Intel Core Series Processor"); /*Flawfinder: ignore*/
- strncat(strCPUName, "Intel Core Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
- break;
- case 0xF: // Model = 0xF: Intel Core 2 Duo processor, model F
- strcpy(CPUInfo.strModel, "Intel Core 2 Series Processor"); /*Flawfinder: ignore*/
- strncat(strCPUName, "Intel Core 2 Series Processor", sizeof(strCPUName)-(strlen(strCPUName)-1)); /*Flawfinder: ignore*/
- break;
- default: // *more bored*
- strcpy(CPUInfo.strModel, "Unknown Intel Pentium Pro/2/3, Core"); /*Flawfinder: ignore*/
- strncat(strCPUName, "Intel Pentium Pro/2/3, Core (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( /* Flawfinder: ignore */
- CPUInfo.strProcessorSerial,
- sizeof(CPUInfo.strProcessorSerial),
- "%04lX-%04lX-%04lX-%04lX-%04lX-%04lX",
- sig1 >> 16,
- sig1 & 0xFFFF,
- sig3 >> 16,
- sig3 & 0xFFFF,
- sig2 >> 16, sig2 & 0xFFFF);
- }
- else
- {
- // If there's no serial number support we just put "No serial number"
- snprintf( /* Flawfinder: ignore */
- CPUInfo.strProcessorSerial,
- sizeof(CPUInfo.strProcessorSerial),
- "No Processor Serial Number");
- }
- // Now we get the standard processor extensions
- GetStandardProcessorExtensions();
+ // Calling __cpuid with 0x80000000 as the InfoType argument
+ // gets the number of valid extended IDs.
+ __cpuid(cpu_info, 0x80000000);
+ unsigned int ext_ids = cpu_info[0];
+ setConfig(eMaxExtID, 0);
- // And finally the processor configuration (caches, TLBs, ...) and translate
- // the data to readable strings
- GetStandardProcessorConfiguration();
- TranslateProcessorConfiguration();
+ char cpu_brand_string[0x40];
+ memset(cpu_brand_string, 0, sizeof(cpu_brand_string));
- // 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;
-
- // 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
+ // Get the information associated with each extended ID.
+ for(unsigned int i=0x80000000; i<=ext_ids; ++i)
{
- 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);
- CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0';
- }
- else
- {
- // Or just tell there is no brand id string support
- strcpy(CPUInfo.strBrandID, ""); /* 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)
+ __cpuid(cpu_info, i);
+
+ // Interpret CPU brand string and cache information.
+ if (i == 0x80000002)
+ memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info));
+ else if (i == 0x80000003)
+ memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info));
+ else if (i == 0x80000004)
{
- 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;
+ memcpy(cpu_brand_string + 32, cpu_info, sizeof(cpu_info));
+ setInfo(eBrandName, cpu_brand_string);
}
- break;
- case 5: // Family = 5: K5 or K6 processor family
- switch (CPUInfo.uiModel)
+ else if (i == 0x80000006)
{
- 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;
+ setConfig(eCacheLineSize, cpu_info[2] & 0xff);
+ setConfig(eL2Associativity, (cpu_info[2] >> 12) & 0xf);
+ setConfig(eCacheSizeK, (cpu_info[2] >> 16) & 0xffff);
}
- 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); /* Flawfinder: ignore */
- 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)
+#elif LL_DARWIN
+
+#include <mach/machine.h>
+#include <sys/sysctl.h>
+
+class LLProcessorInfoDarwinImpl : public LLProcessorInfoImpl
+{
+public:
+ LLProcessorInfoDarwinImpl()
+ {
+ getCPUIDInfo();
+ uint64_t frequency = getSysctlInt64("hw.cpufrequency");
+ setInfo(eFrequency, (F64)frequency / (F64)1000000);
+ }
+
+ virtual ~LLProcessorInfoDarwinImpl() {}
+
+private:
+ int getSysctlInt(const char* name)
+ {
+ int result = 0;
+ size_t len = sizeof(int);
+ int error = sysctlbyname(name, (void*)&result, &len, NULL, 0);
+ return error == -1 ? 0 : result;
+ }
+
+ uint64_t getSysctlInt64(const char* name)
+ {
+ uint64_t value = 0;
+ size_t size = sizeof(value);
+ int result = sysctlbyname(name, (void*)&value, &size, NULL, 0);
+ if ( result == 0 )
+ {
+ if ( size == sizeof( uint64_t ) )
+ ;
+ else if ( size == sizeof( uint32_t ) )
+ value = (uint64_t)(( uint32_t *)&value);
+ else if ( size == sizeof( uint16_t ) )
+ value = (uint64_t)(( uint16_t *)&value);
+ else if ( size == sizeof( uint8_t ) )
+ value = (uint64_t)(( uint8_t *)&value);
+ else
{
- 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;
+ LL_WARNS("Unknown type returned from sysctl!") << LL_ENDL;
}
- 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)
+
+ return result == -1 ? 0 : value;
+ }
+
+ void getCPUIDInfo()
{
- // 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;
+ size_t len = 0;
- // 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;
+ char cpu_brand_string[0x40];
+ len = sizeof(cpu_brand_string);
+ memset(cpu_brand_string, 0, len);
+ sysctlbyname("machdep.cpu.brand_string", (void*)cpu_brand_string, &len, NULL, 0);
+ cpu_brand_string[0x3f] = 0;
+ setInfo(eBrandName, cpu_brand_string);
- // 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
- }
+ char cpu_vendor[0x20];
+ len = sizeof(cpu_vendor);
+ memset(cpu_vendor, 0, len);
+ sysctlbyname("machdep.cpu.vendor", (void*)cpu_vendor, &len, NULL, 0);
+ cpu_vendor[0x1f] = 0;
+ setInfo(eVendor, cpu_vendor);
+
+ setInfo(eStepping, getSysctlInt("machdep.cpu.stepping"));
+ setInfo(eModel, getSysctlInt("machdep.cpu.model"));
+ int family = getSysctlInt("machdep.cpu.family");
+ int ext_family = getSysctlInt("machdep.cpu.extfamily");
+ setInfo(eFamily, family);
+ setInfo(eExtendedFamily, ext_family);
+ setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family));
+ setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel"));
+ setInfo(eBrandID, getSysctlInt("machdep.cpu.brand"));
+ setInfo(eType, 0); // ? where to find this?
+
+ //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8);
+ //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff);
+ setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize"));
+ setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity"));
+ setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size"));
+
+ uint64_t feature_info = getSysctlInt64("machdep.cpu.feature_bits");
+ S32 *feature_infos = (S32*)(&feature_info);
+
+ setConfig(eFeatureBits, feature_infos[0]);
- // 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)
+ for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1)
{
- CPUInfo._L2.bPresent = true;
- snprintf(CPUInfo._L2.strSize, sizeof(CPUInfo._L2.strSize), "%d KB", ecxreg >> 16); /* Flawfinder: ignore */
- switch ((ecxreg >> 12) & 0xF)
+ if(feature_info & bit)
{
- 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;
+ setExtension(cpu_feature_names[index]);
}
- 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 */
-
- // 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
- }
+ // *NOTE:Mani - I didn't find any docs that assure me that machdep.cpu.feature_bits will always be
+ // The feature bits I think it is. Here's a test:
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ #if defined(__i386__) && defined(__PIC__)
+ /* %ebx may be the PIC register. */
+ #define __cpuid(level, a, b, c, d) \
+ __asm__ ("xchgl\t%%ebx, %1\n\t" \
+ "cpuid\n\t" \
+ "xchgl\t%%ebx, %1\n\t" \
+ : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \
+ : "0" (level))
+ #else
+ #define __cpuid(level, a, b, c, d) \
+ __asm__ ("cpuid\n\t" \
+ : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \
+ : "0" (level))
+ #endif
+
+ unsigned int eax, ebx, ecx, edx;
+ __cpuid(0x1, eax, ebx, ecx, edx);
+ if(feature_infos[0] != (S32)edx)
+ {
+ LL_ERRS() << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << LL_ENDL;
+ }
+#endif // LL_RELEASE_FOR_DOWNLOAD
- 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 KuOPs, 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 KuOPs, 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 KuOPs, 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;
+ uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits");
+ S32 *ext_feature_infos = (S32*)(&ext_feature_info);
+ setConfig(eExtFeatureBits, ext_feature_infos[0]);
}
-}
+};
-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 */
+#elif LL_LINUX
+const char CPUINFO_FILE[] = "/proc/cpuinfo";
- // 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)
+class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl
{
- char buf[64]; /* Flawfinder: ignore */
-
- // We just check if the cache is present
- if (cache->bPresent)
+public:
+ LLProcessorInfoLinuxImpl()
{
- // 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 */
+ get_proc_cpuinfo();
}
- 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;
+ virtual ~LLProcessorInfoLinuxImpl() {}
+private:
- // First we check if the processor supports the standard
- // CPUID level 0x00000002
- if (CPUInfo.MaxSupportedLevel >= 2)
+ void get_proc_cpuinfo()
{
- // Now we go read the std. CPUID level 0x00000002 the first time
- unsigned long count, num = 255;
- for (count = 0; count < num; count++)
+ std::map< std::string, std::string > cpuinfo;
+ LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb");
+ if(cpuinfo_fp)
{
- __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)
+ char line[MAX_STRING];
+ memset(line, 0, MAX_STRING);
+ while(fgets(line, MAX_STRING, cpuinfo_fp))
{
- 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);
+ // /proc/cpuinfo on Linux looks like:
+ // name\t*: value\n
+ char* tabspot = strchr( line, '\t' );
+ if (tabspot == NULL)
+ continue;
+ char* colspot = strchr( tabspot, ':' );
+ if (colspot == NULL)
+ continue;
+ char* spacespot = strchr( colspot, ' ' );
+ if (spacespot == NULL)
+ continue;
+ char* nlspot = strchr( line, '\n' );
+ if (nlspot == NULL)
+ nlspot = line + strlen( line ); // Fallback to terminating NUL
+ std::string linename( line, tabspot );
+ std::string llinename(linename);
+ LLStringUtil::toLower(llinename);
+ std::string lineval( spacespot + 1, nlspot );
+ cpuinfo[ llinename ] = lineval;
}
+ fclose(cpuinfo_fp);
+ }
+# if LL_X86
+
+// *NOTE:Mani - eww, macros! srry.
+#define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \
+ if (!cpuinfo[cpuinfo_id].empty()) \
+ { setInfo(llpi_id, cpuinfo[cpuinfo_id]);}
+
+#define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \
+ {\
+ S32 result; \
+ if (!cpuinfo[cpuinfo_id].empty() \
+ && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \
+ { setInfo(llpi_id, result);} \
+ }
+
+ F64 mhz;
+ if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz)
+ && 200.0 < mhz && mhz < 10000.0)
+ {
+ setInfo(eFrequency,(F64)(mhz));
}
- }
-#endif
-}
-// void CProcessor::GetStandardProcessorExtensions()
-// =================================================
-// Private class function to read the standard processor extensions
-///////////////////////////////////////////////////////////////////
-void CProcessor::GetStandardProcessorExtensions()
-{
-#if LL_WINDOWS
- unsigned long ebxreg, edxreg;
+ LLPI_SET_INFO_STRING(eBrandName, "model name");
+ LLPI_SET_INFO_STRING(eVendor, "vendor_id");
- // 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.Altivec_Extensions = false;
- 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
-}
+ LLPI_SET_INFO_INT(eStepping, "stepping");
+ LLPI_SET_INFO_INT(eModel, "model");
-// 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;
+
+ S32 family;
+ if (!cpuinfo["cpu family"].empty()
+ && LLStringUtil::convertToS32(cpuinfo["cpu family"], family))
+ {
+ setInfo(eFamily, family);
+ }
- // 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;
- // Null terminate for string comparisons below.
- CPUInfo.strVendor[12] = 0;
-
- // 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
- // See http://www.sandpile.org/ia32/cpuid.htm
- if (!strcmp(CPUInfo.strVendor, "GenuineIntel"))
- {
- AnalyzeIntelProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "AuthenticAMD"))
- {
- AnalyzeAMDProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "UMC UMC UMC"))
- {
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "CyrixInstead"))
- {
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "NexGenDriven"))
- {
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "CentaurHauls"))
- {
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "RiseRiseRise"))
- {
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "SiS SiS SiS"))
- {
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "GenuineTMx86"))
- {
- // Transmeta
- AnalyzeUnknownProcessor();
- }
- else if (!strcmp(CPUInfo.strVendor, "Geode by NSC"))
- {
- AnalyzeUnknownProcessor();
- }
- else
- {
- AnalyzeUnknownProcessor();
- }
-#endif
- // After all we return the class CPUInfo member var
- return (&CPUInfo);
-}
-
-#elif LL_SOLARIS
-#include <kstat.h>
+ setInfo(eFamilyName, compute_CPUFamilyName(cpuinfo["vendor_id"].c_str(), family, 0));
-#if defined(__i386)
-#include <sys/auxv.h>
-#endif
-
-// ======================
-// Class constructor:
-/////////////////////////
-CProcessor::CProcessor()
-{
- uqwFrequency = 0;
- strCPUName[0] = 0;
- memset(&CPUInfo, 0, sizeof(CPUInfo));
-}
+ // setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel"));
+ // setInfo(eBrandID, getSysctlInt("machdep.cpu.brand"));
+ // setInfo(eType, 0); // ? where to find this?
-// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
-// =========================================================================
-// Function to query the current CPU frequency
-////////////////////////////////////////////////////////////////////////////
-F64 CProcessor::GetCPUFrequency(unsigned int /*uiMeasureMSecs*/)
-{
- if(uqwFrequency == 0){
- GetCPUInfo();
- }
-
- return uqwFrequency;
-}
-
-// const ProcessorInfo *CProcessor::GetCPUInfo()
-// =============================================
-// Calls all the other detection function to create an detailed
-// processor information
-///////////////////////////////////////////////////////////////
-const ProcessorInfo *CProcessor::GetCPUInfo()
-{
- // In Solaris the CPU info is in the kstats
- // try "psrinfo" or "kstat cpu_info" to see all
- // that's available
- int ncpus=0, i;
- kstat_ctl_t *kc;
- kstat_t *ks;
- kstat_named_t *ksinfo, *ksi;
- kstat_t *CPU_stats_list;
-
- kc = kstat_open();
-
- if((int)kc == -1){
- llwarns << "kstat_open(0 failed!" << llendl;
- return (&CPUInfo);
- }
-
- for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next) {
- if (strncmp(ks->ks_module, "cpu_info", 8) == 0 &&
- strncmp(ks->ks_name, "cpu_info", 8) == 0)
- ncpus++;
- }
-
- if(ncpus < 1){
- llwarns << "No cpus found in kstats!" << llendl;
- return (&CPUInfo);
- }
-
- for (ks = kc->kc_chain; ks; ks = ks->ks_next) {
- if (strncmp(ks->ks_module, "cpu_info", 8) == 0
- && strncmp(ks->ks_name, "cpu_info", 8) == 0
- && kstat_read(kc, ks, NULL) != -1){
- CPU_stats_list = ks; // only looking at the first CPU
-
- break;
- }
- }
-
- if(ncpus > 1)
- snprintf(strCPUName, sizeof(strCPUName), "%d x ", ncpus);
-
- kstat_read(kc, CPU_stats_list, NULL);
- ksinfo = (kstat_named_t *)CPU_stats_list->ks_data;
- for(i=0; i < (int)(CPU_stats_list->ks_ndata); ++i){ // Walk the kstats for this cpu gathering what we need
- ksi = ksinfo++;
- if(!strcmp(ksi->name, "brand")){
- strncat(strCPUName, (char *)KSTAT_NAMED_STR_PTR(ksi),
- sizeof(strCPUName)-strlen(strCPUName)-1);
- strncat(CPUInfo.strFamily, (char *)KSTAT_NAMED_STR_PTR(ksi),
- sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1);
- strncpy(CPUInfo.strBrandID, strCPUName,sizeof(CPUInfo.strBrandID)-1);
- CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0';
- // DEBUG llinfos << "CPU brand: " << strCPUName << llendl;
- continue;
- }
+ //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8);
+ //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff);
+ //setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize"));
+ //setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity"));
+ //setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size"));
+
+ // Read extensions
+ std::string flags = " " + cpuinfo["flags"] + " ";
+ LLStringUtil::toLower(flags);
- if(!strcmp(ksi->name, "clock_MHz")){
-#if defined(__sparc)
- llinfos << "Raw kstat clock rate is: " << ksi->value.l << llendl;
- uqwFrequency = (F64)(ksi->value.l * 1000000);
-#else
- uqwFrequency = (F64)(ksi->value.i64 * 1000000);
-#endif
- //DEBUG llinfos << "CPU frequency: " << uqwFrequency << llendl;
- continue;
+ if( flags.find( " sse " ) != std::string::npos )
+ {
+ setExtension(cpu_feature_names[eSSE_Ext]);
}
-#if defined(__i386)
- if(!strcmp(ksi->name, "vendor_id")){
- strncpy(CPUInfo.strVendor, (char *)KSTAT_NAMED_STR_PTR(ksi), sizeof(CPUInfo.strVendor)-1);
- // DEBUG llinfos << "CPU vendor: " << CPUInfo.strVendor << llendl;
- continue;
+ if( flags.find( " sse2 " ) != std::string::npos )
+ {
+ setExtension(cpu_feature_names[eSSE2_Ext]);
}
-#endif
- }
-
- kstat_close(kc);
-
-#if defined(__sparc) // SPARC does not define a vendor string in kstat
- strncpy(CPUInfo.strVendor, "Sun Microsystems, Inc.", sizeof(CPUInfo.strVendor)-1);
-#endif
-
- // DEBUG llinfo << "The system has " << ncpus << " CPUs with a clock rate of " << uqwFrequency << "MHz." << llendl;
-
-#if defined (__i386) // we really don't care about the CPU extensions on SPARC but on x86...
-
- // Now get cpu extensions
-
- uint_t ui;
-
- (void) getisax(&ui, 1);
-
- if(ui & AV_386_FPU)
- CPUInfo._Ext.FPU_FloatingPointUnit = true;
- if(ui & AV_386_CX8)
- CPUInfo._Ext.CX8_COMPXCHG8B_Instruction = true;
- if(ui & AV_386_MMX)
- CPUInfo._Ext.MMX_MultimediaExtensions = true;
- if(ui & AV_386_AMD_MMX)
- CPUInfo._Ext.MMX_MultimediaExtensions = true;
- if(ui & AV_386_FXSR)
- CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = true;
- if(ui & AV_386_SSE)
- CPUInfo._Ext.SSE_StreamingSIMD_Extensions = true;
- if(ui & AV_386_SSE2)
- CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = true;
-/* Left these here since they may get used later
- if(ui & AV_386_SSE3)
- CPUInfo._Ext.... = true;
- if(ui & AV_386_AMD_3DNow)
- CPUInfo._Ext.... = true;
- if(ui & AV_386_AMD_3DNowx)
- CPUInfo._Ext.... = true;
-*/
-#endif
- 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;
- strCPUName[0] = 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;
+# endif // LL_X86
}
-
- 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)
+ std::string getCPUFeatureDescription() const
{
- 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);
+ std::ostringstream s;
- 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;
- }
-
- CPUInfo._Ext.EMMX_MultimediaExtensions =
- CPUInfo._Ext.MMX_MultimediaExtensions =
- CPUInfo._Ext.SSE_StreamingSIMD_Extensions =
- CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = false;
-
- CPUInfo._Ext.Altivec_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.Altivec_Extensions = false;
- 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;
+ // *NOTE:Mani - This is for linux only.
+ LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb");
+ if(cpuinfo)
+ {
+ char line[MAX_STRING];
+ memset(line, 0, MAX_STRING);
+ while(fgets(line, MAX_STRING, cpuinfo))
+ {
+ line[strlen(line)-1] = ' ';
+ s << line;
+ s << std::endl;
+ }
+ fclose(cpuinfo);
+ s << std::endl;
+ }
+ else
+ {
+ s << "Unable to collect processor information" << std::endl;
+ }
+ return s.str();
}
+
+};
- 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;
- }
+#endif // LL_MSVC elif LL_DARWIN elif LL_LINUX
- 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;
+//////////////////////////////////////////////////////
+// Interface definition
+LLProcessorInfo::LLProcessorInfo() : mImpl(NULL)
+{
+ // *NOTE:Mani - not thread safe.
+ if(!mImpl)
+ {
+#ifdef LL_MSVC
+ static LLProcessorInfoWindowsImpl the_impl;
+ mImpl = &the_impl;
+#elif LL_DARWIN
+ static LLProcessorInfoDarwinImpl the_impl;
+ mImpl = &the_impl;
+#else
+ static LLProcessorInfoLinuxImpl the_impl;
+ mImpl = &the_impl;
+#endif // LL_MSVC
}
-
- 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");
- }
-#if !LL_SOLARIS // NOTE: Why bother printing all this when it's irrelavent
-
- 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("ALTVEC Altivec Extensions: ", CPUInfo._Ext.Altivec_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);
-#endif
- // Yippie!!!
- return true;
-}
+LLProcessorInfo::~LLProcessorInfo() {}
+F64MegahertzImplicit LLProcessorInfo::getCPUFrequency() const { return mImpl->getCPUFrequency(); }
+bool LLProcessorInfo::hasSSE() const { return mImpl->hasSSE(); }
+bool LLProcessorInfo::hasSSE2() const { return mImpl->hasSSE2(); }
+bool LLProcessorInfo::hasAltivec() const { return mImpl->hasAltivec(); }
+std::string LLProcessorInfo::getCPUFamilyName() const { return mImpl->getCPUFamilyName(); }
+std::string LLProcessorInfo::getCPUBrandName() const { return mImpl->getCPUBrandName(); }
+std::string LLProcessorInfo::getCPUFeatureDescription() const { return mImpl->getCPUFeatureDescription(); }
-// bool CProcessor::WriteInfoTextFile(const std::string& strFilename)
-// ===========================================================
-// Takes use of CProcessor::CPUInfoToText and saves the string to a
-// file
-///////////////////////////////////////////////////////////////////
-bool CProcessor::WriteInfoTextFile(const std::string& 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)
- LLFILE *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
index 746d007a7f..90e5bc59ee 100644..100755
--- a/indra/llcommon/llprocessor.h
+++ b/indra/llcommon/llprocessor.h
@@ -2,195 +2,50 @@
* @file llprocessor.h
* @brief Code to figure out the processor. Originally by Benjamin Jurke.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-// Author: Benjamin Jurke
-// File history: 27.02.2002 File created.
-///////////////////////////////////////////
-
#ifndef LLPROCESSOR_H
#define LLPROCESSOR_H
+#include "llunits.h"
-// Options:
-///////////
-#if LL_WINDOWS
-#define PROCESSOR_FREQUENCY_MEASURE_AVAILABLE
-#endif
-
-#if LL_MSVC && _M_X64
-# define LL_X86_64 1
-# define LL_X86 1
-#elif LL_MSVC && _M_IX86
-# define LL_X86 1
-#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
-# define LL_X86_64 1
-# define LL_X86 1
-#elif LL_GNUC && ( defined(__i386__) )
-# define LL_X86 1
-#elif LL_GNUC && ( defined(__powerpc__) || defined(__ppc__) )
-# define LL_PPC 1
-#endif
-
-
-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 Altivec_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;
-};
-
-struct ProcessorCache
-{
- bool bPresent;
- char strSize[32]; /* Flawfinder: ignore */
- unsigned int uiAssociativeWays;
- unsigned int uiLineSize;
- bool bSectored;
- char strCache[128]; /* Flawfinder: ignore */
-};
-
-struct ProcessorL1Cache
-{
- ProcessorCache Instruction;
- ProcessorCache Data;
-};
-
-struct ProcessorTLB
-{
- bool bPresent;
- char strPageSize[32]; /* Flawfinder: ignore */
- unsigned int uiAssociativeWays;
- unsigned int uiEntries;
- char strTLB[128]; /* Flawfinder: ignore */
-};
-
-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;
-};
-
+class LLProcessorInfoImpl;
-// CProcessor
-// ==========
-// Class for detecting the processor name, type and available
-// extensions as long as it's speed.
-/////////////////////////////////////////////////////////////
-class CProcessor
+class LL_COMMON_API LLProcessorInfo
{
-// Constructor / Destructor:
-////////////////////////////
-public:
- CProcessor();
-
-// Private vars:
-////////////////
public:
- F64 uqwFrequency;
- char strCPUName[128]; /* Flawfinder: ignore */
- ProcessorInfo CPUInfo;
-
-// Private functions:
-/////////////////////
+ LLProcessorInfo();
+ ~LLProcessorInfo();
+
+ F64MegahertzImplicit getCPUFrequency() const;
+ bool hasSSE() const;
+ bool hasSSE2() const;
+ bool hasAltivec() const;
+ std::string getCPUFamilyName() const;
+ std::string getCPUBrandName() const;
+ std::string getCPUFeatureDescription() const;
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 std::string& strFilename);
+ LLProcessorInfoImpl* mImpl;
};
-
-#endif
+#endif // LLPROCESSOR_H
diff --git a/indra/llcommon/llprocinfo.cpp b/indra/llcommon/llprocinfo.cpp
new file mode 100644
index 0000000000..c00f979b0b
--- /dev/null
+++ b/indra/llcommon/llprocinfo.cpp
@@ -0,0 +1,94 @@
+/**
+* @file llprocinfo.cpp
+* @brief Process, cpu and resource usage information APIs.
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+
+#include "llprocinfo.h"
+
+#if LL_WINDOWS
+
+#define PSAPI_VERSION 1
+#include "windows.h"
+#include "psapi.h"
+
+#elif LL_DARWIN
+
+#include <sys/resource.h>
+#include <mach/mach.h>
+
+#else
+
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#endif // LL_WINDOWS/LL_DARWIN
+
+
+// static
+void LLProcInfo::getCPUUsage(time_type & user_time, time_type & system_time)
+{
+#if LL_WINDOWS
+
+ HANDLE self(GetCurrentProcess()); // Does not have to be closed
+ FILETIME ft_dummy, ft_system, ft_user;
+
+ GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user);
+ ULARGE_INTEGER uli;
+ uli.u.LowPart = ft_system.dwLowDateTime;
+ uli.u.HighPart = ft_system.dwHighDateTime;
+ system_time = uli.QuadPart / U64L(10); // Convert to uS
+ uli.u.LowPart = ft_user.dwLowDateTime;
+ uli.u.HighPart = ft_user.dwHighDateTime;
+ user_time = uli.QuadPart / U64L(10);
+
+#elif LL_DARWIN
+
+ struct rusage usage;
+
+ if (getrusage(RUSAGE_SELF, &usage))
+ {
+ user_time = system_time = time_type(0U);
+ return;
+ }
+ user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
+ system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
+
+#else // Linux
+
+ struct rusage usage;
+
+ if (getrusage(RUSAGE_SELF, &usage))
+ {
+ user_time = system_time = time_type(0U);
+ return;
+ }
+ user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec;
+ system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec;
+
+#endif // LL_WINDOWS/LL_DARWIN/Linux
+}
+
+
diff --git a/indra/llcommon/llprocinfo.h b/indra/llcommon/llprocinfo.h
new file mode 100644
index 0000000000..e78bcf490a
--- /dev/null
+++ b/indra/llcommon/llprocinfo.h
@@ -0,0 +1,68 @@
+/**
+* @file llprocinfo.h
+* @brief Interface to process/cpu/resource information services.
+* @author monty@lindenlab.com
+*
+* $LicenseInfo:firstyear=2013&license=viewerlgpl$
+* Second Life Viewer Source Code
+* Copyright (C) 2013, Linden Research, Inc.
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation;
+* version 2.1 of the License only.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with this library; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*
+* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+* $/LicenseInfo$
+*/
+
+#ifndef LL_PROCINFO_H
+#define LL_PROCINFO_H
+
+
+#include "linden_common.h"
+
+/// @file llprocinfo.h
+///
+/// Right now, this is really a namespace disguised as a class.
+/// It wraps some types and functions to return information about
+/// process resource consumption in a non-OS-specific manner.
+///
+/// Threading: No instances so that's thread-safe. Implementations
+/// of static functions should be thread-safe, they mostly involve
+/// direct syscall invocations.
+///
+/// Allocation: Not instantiatable.
+
+class LL_COMMON_API LLProcInfo
+{
+public:
+ /// Public types
+
+ typedef U64 time_type; /// Relative microseconds
+
+private:
+ LLProcInfo(); // Not defined
+ ~LLProcInfo(); // Not defined
+ LLProcInfo(const LLProcInfo &); // Not defined
+ void operator=(const LLProcInfo &); // Not defined
+
+public:
+ /// Get accumulated system and user CPU time in
+ /// microseconds. Syscalls involved in every invocation.
+ ///
+ /// Threading: expected to be safe.
+ static void getCPUUsage(time_type & user_time, time_type & system_time);
+};
+
+
+#endif // LL_PROCINFO_H
diff --git a/indra/llcommon/llptrskiplist.h b/indra/llcommon/llptrskiplist.h
deleted file mode 100644
index 68781657d7..0000000000
--- a/indra/llcommon/llptrskiplist.h
+++ /dev/null
@@ -1,730 +0,0 @@
-/**
- * @file llptrskiplist.h
- * @brief Skip list implementation.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLPTRSKIPLIST_H
-#define LL_LLPTRSKIPLIST_H
-
-#include "llerror.h"
-#include "llrand.h"
-//#include "vmath.h"
-#include "llrand.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 (ll_frand() < 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
deleted file mode 100644
index 20d0019901..0000000000
--- a/indra/llcommon/llptrskipmap.h
+++ /dev/null
@@ -1,1245 +0,0 @@
-/**
- * @file llptrskipmap.h
- * @brief Just like a LLSkipMap, but since it's pointers, you can call
- * deleteAllData
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LL_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),
- mNumberOfSteps(0)
-{
- 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),
- mNumberOfSteps(0)
-{
- 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 (ll_frand() < 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 (ll_frand() < 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 (ll_frand() < 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/llptrto.cpp b/indra/llcommon/llptrto.cpp
index ce93f09489..b270291bd6 100644..100755
--- a/indra/llcommon/llptrto.cpp
+++ b/indra/llcommon/llptrto.cpp
@@ -4,31 +4,25 @@
* @date 2008-08-20
* @brief Test for llptrto.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llptrto.h b/indra/llcommon/llptrto.h
index 74c117a7f6..4082e30de6 100644..100755
--- a/indra/llcommon/llptrto.h
+++ b/indra/llcommon/llptrto.h
@@ -6,31 +6,25 @@
* TARGET is a subclass of LLRefCount or LLThreadSafeRefCount --
* LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,7 +33,6 @@
#include "llpointer.h"
#include "llrefcount.h" // LLRefCount
-#include "llthread.h" // LLThreadSafeRefCount
#include <boost/type_traits/is_base_of.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/utility/enable_if.hpp>
diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp
index 06ceeb2bc3..8cef4293cd 100644..100755
--- a/indra/llcommon/llqueuedthread.cpp
+++ b/indra/llcommon/llqueuedthread.cpp
@@ -1,31 +1,25 @@
/**
* @file llqueuedthread.cpp
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,11 +28,12 @@
#include "llstl.h"
#include "lltimer.h" // ms_sleep()
+#include "lltracethreadrecorder.h"
//============================================================================
// MAIN THREAD
-LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
+LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
LLThread(name),
mThreaded(threaded),
mIdleThread(TRUE),
@@ -47,6 +42,11 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) :
{
if (mThreaded)
{
+ if(should_pause)
+ {
+ pause() ; //call this before start the thread.
+ }
+
start();
}
}
@@ -81,7 +81,7 @@ void LLQueuedThread::shutdown()
}
if (timeout == 0)
{
- llwarns << "~LLQueuedThread (" << mName << ") timed out!" << llendl;
+ LL_WARNS() << "~LLQueuedThread (" << mName << ") timed out!" << LL_ENDL;
}
}
else
@@ -102,7 +102,7 @@ void LLQueuedThread::shutdown()
}
if (active_count)
{
- llwarns << "~LLQueuedThread() called with active requests: " << active_count << llendl;
+ LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
}
}
@@ -110,7 +110,7 @@ void LLQueuedThread::shutdown()
// MAIN THREAD
// virtual
-S32 LLQueuedThread::update(U32 max_time_ms)
+S32 LLQueuedThread::update(F32 max_time_ms)
{
if (!mStarted)
{
@@ -123,7 +123,7 @@ S32 LLQueuedThread::update(U32 max_time_ms)
return updateQueue(max_time_ms);
}
-S32 LLQueuedThread::updateQueue(U32 max_time_ms)
+S32 LLQueuedThread::updateQueue(F32 max_time_ms)
{
F64 max_time = (F64)max_time_ms * .001;
LLTimer timer;
@@ -133,8 +133,11 @@ S32 LLQueuedThread::updateQueue(U32 max_time_ms)
if (mThreaded)
{
pending = getPending();
+ if(pending > 0)
+ {
unpause();
}
+ }
else
{
while (pending > 0)
@@ -196,11 +199,11 @@ void LLQueuedThread::printQueueStats()
if (!mRequestQueue.empty())
{
QueuedRequest *req = *mRequestQueue.begin();
- llinfos << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << llendl;
+ LL_INFOS() << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << LL_ENDL;
}
else
{
- llinfos << "Queued Thread Idle" << llendl;
+ LL_INFOS() << "Queued Thread Idle" << LL_ENDL;
}
unlockData();
}
@@ -231,7 +234,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
mRequestQueue.insert(req);
mRequestHash.insert(req);
#if _DEBUG
-// llinfos << llformat("LLQueuedThread::Added req [%08d]",handle) << llendl;
+// LL_INFOS() << llformat("LLQueuedThread::Added req [%08d]",handle) << LL_ENDL;
#endif
unlockData();
@@ -243,7 +246,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req)
// MAIN thread
bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
{
- llassert (handle != nullHandle())
+ llassert (handle != nullHandle());
bool res = false;
bool waspaused = isPaused();
bool done = false;
@@ -362,7 +365,7 @@ bool LLQueuedThread::completeRequest(handle_t handle)
llassert_always(req->getStatus() != STATUS_QUEUED);
llassert_always(req->getStatus() != STATUS_INPROGRESS);
#if _DEBUG
-// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
+// LL_INFOS() << llformat("LLQueuedThread::Completed req [%08d]",handle) << LL_ENDL;
#endif
mRequestHash.erase(handle);
req->deleteRequest();
@@ -383,7 +386,7 @@ bool LLQueuedThread::check()
{
if (entry->getHashKey() > mNextHandle)
{
- llerrs << "Hash Error" << llendl;
+ LL_ERRS() << "Hash Error" << LL_ENDL;
return false;
}
entry = entry->getNextEntry();
@@ -401,6 +404,7 @@ S32 LLQueuedThread::processNextRequest()
QueuedRequest *req;
// Get next request from pool
lockData();
+
while(1)
{
req = NULL;
@@ -425,9 +429,11 @@ S32 LLQueuedThread::processNextRequest()
llassert_always(req->getStatus() == STATUS_QUEUED);
break;
}
+ U32 start_priority = 0 ;
if (req)
{
req->setStatus(STATUS_INPROGRESS);
+ start_priority = req->getPriority();
}
unlockData();
@@ -436,8 +442,7 @@ S32 LLQueuedThread::processNextRequest()
// safe to access req.
if (req)
{
- // process request
- U32 start_priority = req->getPriority();
+ // process request
bool complete = req->processRequest();
if (complete)
@@ -459,15 +464,16 @@ S32 LLQueuedThread::processNextRequest()
req->setStatus(STATUS_QUEUED);
mRequestQueue.insert(req);
unlockData();
- if (mThreaded && start_priority <= PRIORITY_LOW)
+ if (mThreaded && start_priority < PRIORITY_NORMAL)
{
ms_sleep(1); // sleep the thread a little
}
}
+
+ LLTrace::get_thread_recorder()->pushToParent();
}
S32 pending = getPending();
-
return pending;
}
@@ -496,6 +502,7 @@ void LLQueuedThread::run()
if (isQuitting())
{
+ LLTrace::get_thread_recorder()->pushToParent();
endThread();
break;
}
@@ -504,15 +511,16 @@ void LLQueuedThread::run()
threadedUpdate();
- int res = processNextRequest();
- if (res == 0)
+ int pending_work = processNextRequest();
+
+ if (pending_work == 0)
{
mIdleThread = TRUE;
ms_sleep(1);
}
//LLThread::yield(); // thread should yield after each request
}
- llinfos << "LLQueuedThread " << mName << " EXITING." << llendl;
+ LL_INFOS() << "LLQueuedThread " << mName << " EXITING." << LL_ENDL;
}
// virtual
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index 9a9dbb18cc..d3704b0fe2 100644..100755
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -2,31 +2,25 @@
* @file llqueuedthread.h
* @brief
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -155,7 +149,7 @@ public:
static handle_t nullHandle() { return handle_t(0); }
public:
- LLQueuedThread(const std::string& name, bool threaded = true);
+ LLQueuedThread(const std::string& name, bool threaded = true, bool should_pause = false);
virtual ~LLQueuedThread();
virtual void shutdown();
@@ -179,13 +173,13 @@ protected:
public:
bool waitForResult(handle_t handle, bool auto_complete = true);
- virtual S32 update(U32 max_time_ms);
- S32 updateQueue(U32 max_time_ms);
+ virtual S32 update(F32 max_time_ms);
+ S32 updateQueue(F32 max_time_ms);
void waitOnPending();
void printQueueStats();
- S32 getPending();
+ virtual S32 getPending();
bool getThreaded() { return mThreaded ? true : false; }
// Request accessors
diff --git a/indra/llcommon/llrand.cpp b/indra/llcommon/llrand.cpp
index 0b2f8b8036..cb28a8f5c3 100644..100755
--- a/indra/llcommon/llrand.cpp
+++ b/indra/llcommon/llrand.cpp
@@ -2,31 +2,25 @@
* @file llrand.cpp
* @brief Global random generator.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llrand.h b/indra/llcommon/llrand.h
index 30fec9b982..ad317d5bf7 100644..100755
--- a/indra/llcommon/llrand.h
+++ b/indra/llcommon/llrand.h
@@ -2,31 +2,25 @@
* @file llrand.h
* @brief Information, functions, and typedefs for randomness.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp
index c90b52f482..a638df2c7c 100644..100755
--- a/indra/llcommon/llrefcount.cpp
+++ b/indra/llcommon/llrefcount.cpp
@@ -2,31 +2,25 @@
* @file llrefcount.cpp
* @brief Base class for reference counted objects for use with LLPointer
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
@@ -35,9 +29,25 @@
#include "llerror.h"
+#if LL_REF_COUNT_DEBUG
+#include "llthread.h"
+#include "llapr.h"
+#endif
+
LLRefCount::LLRefCount(const LLRefCount& other)
: mRef(0)
{
+#if LL_REF_COUNT_DEBUG
+ if(gAPRPoolp)
+ {
+ mMutexp = new LLMutex(gAPRPoolp) ;
+ }
+ else
+ {
+ mMutexp = NULL ;
+ }
+ mCrashAtUnlock = FALSE ;
+#endif
}
LLRefCount& LLRefCount::operator=(const LLRefCount&)
@@ -49,12 +59,106 @@ LLRefCount& LLRefCount::operator=(const LLRefCount&)
LLRefCount::LLRefCount() :
mRef(0)
{
+#if LL_REF_COUNT_DEBUG
+ if(gAPRPoolp)
+ {
+ mMutexp = new LLMutex(gAPRPoolp) ;
+ }
+ else
+ {
+ mMutexp = NULL ;
+ }
+ mCrashAtUnlock = FALSE ;
+#endif
}
LLRefCount::~LLRefCount()
{
if (mRef != 0)
{
- llerrs << "deleting non-zero reference" << llendl;
+ LL_ERRS() << "deleting non-zero reference" << LL_ENDL;
+ }
+
+#if LL_REF_COUNT_DEBUG
+ if(gAPRPoolp)
+ {
+ delete mMutexp ;
}
+#endif
}
+
+#if LL_REF_COUNT_DEBUG
+void LLRefCount::ref() const
+{
+ if(mMutexp)
+ {
+ if(mMutexp->isLocked())
+ {
+ mCrashAtUnlock = TRUE ;
+ LL_ERRS() << "the mutex is locked by the thread: " << mLockedThreadID
+ << " Current thread: " << LLThread::currentID() << LL_ENDL ;
+ }
+
+ mMutexp->lock() ;
+ mLockedThreadID = LLThread::currentID() ;
+
+ mRef++;
+
+ if(mCrashAtUnlock)
+ {
+ while(1); //crash here.
+ }
+ mMutexp->unlock() ;
+ }
+ else
+ {
+ mRef++;
+ }
+}
+
+S32 LLRefCount::unref() const
+{
+ if(mMutexp)
+ {
+ if(mMutexp->isLocked())
+ {
+ mCrashAtUnlock = TRUE ;
+ LL_ERRS() << "the mutex is locked by the thread: " << mLockedThreadID
+ << " Current thread: " << LLThread::currentID() << LL_ENDL ;
+ }
+
+ mMutexp->lock() ;
+ mLockedThreadID = LLThread::currentID() ;
+
+ llassert(mRef >= 1);
+ if (0 == --mRef)
+ {
+ if(mCrashAtUnlock)
+ {
+ while(1); //crash here.
+ }
+ mMutexp->unlock() ;
+
+ delete this;
+ return 0;
+ }
+
+ if(mCrashAtUnlock)
+ {
+ while(1); //crash here.
+ }
+ mMutexp->unlock() ;
+ return mRef;
+ }
+ else
+ {
+ llassert(mRef >= 1);
+ if (0 == --mRef)
+ {
+ delete this;
+ return 0;
+ }
+ return mRef;
+ }
+}
+#endif
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index a18f6706a9..3836a9b5fb 100644..100755
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -2,37 +2,39 @@
* @file llrefcount.h
* @brief Base class for reference counted objects for use with LLPointer
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLREFCOUNT_H
#define LLREFCOUNT_H
#include <boost/noncopyable.hpp>
+#include <boost/intrusive_ptr.hpp>
+#include "llmutex.h"
+#include "llapr.h"
+
+#define LL_REF_COUNT_DEBUG 0
+#if LL_REF_COUNT_DEBUG
+class LLMutex ;
+#endif
//----------------------------------------------------------------------------
// RefCount objects should generally only be accessed by way of LLPointer<>'s
@@ -49,21 +51,26 @@ protected:
public:
LLRefCount();
- void ref() const
+#if LL_REF_COUNT_DEBUG
+ void ref() const ;
+ S32 unref() const ;
+#else
+ inline void ref() const
{
mRef++;
}
- S32 unref() const
+ inline S32 unref() const
{
llassert(mRef >= 1);
- if (0 == --mRef)
+ if (0 == --mRef)
{
delete this;
return 0;
}
return mRef;
}
+#endif
//NOTE: when passing around a const LLRefCount object, this can return different results
// at different types, since mRef is mutable
@@ -74,6 +81,95 @@ public:
private:
mutable S32 mRef;
+
+#if LL_REF_COUNT_DEBUG
+ LLMutex* mMutexp ;
+ mutable U32 mLockedThreadID ;
+ mutable BOOL mCrashAtUnlock ;
+#endif
+};
+
+
+//============================================================================
+
+// see llmemory.h for LLPointer<> definition
+
+class LL_COMMON_API LLThreadSafeRefCount
+{
+public:
+ static void initThreadSafeRefCount(); // creates sMutex
+ static void cleanupThreadSafeRefCount(); // destroys sMutex
+
+private:
+ static LLMutex* sMutex;
+
+protected:
+ virtual ~LLThreadSafeRefCount(); // use unref()
+
+public:
+ LLThreadSafeRefCount();
+ LLThreadSafeRefCount(const LLThreadSafeRefCount&);
+ LLThreadSafeRefCount& operator=(const LLThreadSafeRefCount& ref)
+ {
+ mRef = 0;
+ return *this;
+ }
+
+ void ref()
+ {
+ mRef++;
+ }
+
+ void unref()
+ {
+ llassert(mRef >= 1);
+ if ((--mRef) == 0) // See note in llapr.h on atomic decrement operator return value.
+ {
+ // If we hit zero, the caller should be the only smart pointer owning the object and we can delete it.
+ // It is technically possible for a vanilla pointer to mess this up, or another thread to
+ // jump in, find this object, create another smart pointer and end up dangling, but if
+ // the code is that bad and not thread-safe, it's trouble already.
+ delete this;
+ }
+ }
+
+ S32 getNumRefs() const
+ {
+ const S32 currentVal = mRef.CurrentValue();
+ return currentVal;
+ }
+
+private:
+ LLAtomic32< S32 > mRef;
};
+/**
+ * intrusive pointer support
+ * this allows you to use boost::intrusive_ptr with any LLRefCount-derived type
+ */
+/**
+ * intrusive pointer support for LLThreadSafeRefCount
+ * this allows you to use boost::intrusive_ptr with any LLThreadSafeRefCount-derived type
+ */
+
+inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p)
+{
+ p->ref();
+}
+
+inline void intrusive_ptr_release(LLThreadSafeRefCount* p)
+{
+ p->unref();
+}
+
+inline void intrusive_ptr_add_ref(LLRefCount* p)
+{
+ p->ref();
+}
+
+inline void intrusive_ptr_release(LLRefCount* p)
+{
+ p->unref();
+}
+
#endif
diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h
new file mode 100755
index 0000000000..29950c108d
--- /dev/null
+++ b/indra/llcommon/llregistry.h
@@ -0,0 +1,358 @@
+/**
+ * @file llregistry.h
+ * @brief template classes for registering name, value pairs in nested scopes, statically, etc.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLREGISTRY_H
+#define LL_LLREGISTRY_H
+
+#include <list>
+
+#include "llsingleton.h"
+#include "llstl.h"
+
+template <typename T>
+struct LLRegistryDefaultComparator
+{
+ bool operator()(const T& lhs, const T& rhs) const
+ {
+ using std::less;
+ return less<T>()(lhs, rhs);
+ }
+};
+
+template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistry
+{
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef const KEY& ref_const_key_t;
+ typedef const VALUE& ref_const_value_t;
+ typedef const VALUE* ptr_const_value_t;
+ typedef VALUE* ptr_value_t;
+
+ class Registrar
+ {
+ friend class LLRegistry<KEY, VALUE, COMPARATOR>;
+ public:
+ typedef std::map<KEY, VALUE, COMPARATOR> registry_map_t;
+
+ bool add(ref_const_key_t key, ref_const_value_t value)
+ {
+ if (mMap.insert(std::make_pair(key, value)).second == false)
+ {
+ LL_WARNS() << "Tried to register " << key << " but it was already registered!" << LL_ENDL;
+ return false;
+ }
+ return true;
+ }
+
+ void remove(ref_const_key_t key)
+ {
+ mMap.erase(key);
+ }
+
+ void replace(ref_const_key_t key, ref_const_value_t value)
+ {
+ mMap[key] = value;
+ }
+
+ typename registry_map_t::const_iterator beginItems() const
+ {
+ return mMap.begin();
+ }
+
+ typename registry_map_t::const_iterator endItems() const
+ {
+ return mMap.end();
+ }
+
+ protected:
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ typename registry_map_t::iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ typename registry_map_t::const_iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ // if the registry is used to store pointers, and null values are valid entries
+ // then use this function to check the existence of an entry
+ bool exists(ref_const_key_t key) const
+ {
+ return mMap.find(key) != mMap.end();
+ }
+
+ bool empty() const
+ {
+ return mMap.empty();
+ }
+
+ protected:
+ // use currentRegistrar() or defaultRegistrar()
+ Registrar() {}
+ ~Registrar() {}
+
+ private:
+ registry_map_t mMap;
+ };
+
+ typedef typename std::list<Registrar*> scope_list_t;
+ typedef typename std::list<Registrar*>::iterator scope_list_iterator_t;
+ typedef typename std::list<Registrar*>::const_iterator scope_list_const_iterator_t;
+
+ LLRegistry()
+ {}
+
+ ~LLRegistry() {}
+
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ for(scope_list_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ ptr_value_t valuep = (*it)->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ ptr_value_t valuep = (*it)->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ bool exists(ref_const_key_t key) const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ if ((*it)->exists(key)) return true;
+ }
+
+ return mDefaultRegistrar.exists(key);
+ }
+
+ bool empty() const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ if (!(*it)->empty()) return false;
+ }
+
+ return mDefaultRegistrar.empty();
+ }
+
+
+ Registrar& defaultRegistrar()
+ {
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& defaultRegistrar() const
+ {
+ return mDefaultRegistrar;
+ }
+
+
+ Registrar& currentRegistrar()
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& currentRegistrar() const
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+
+protected:
+ void addScope(Registrar* scope)
+ {
+ // newer scopes go up front
+ mActiveScopes.insert(mActiveScopes.begin(), scope);
+ }
+
+ void removeScope(Registrar* scope)
+ {
+ // O(N) but should be near the beggining and N should be small and this is safer than storing iterators
+ scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope);
+ if (iter != mActiveScopes.end())
+ {
+ mActiveScopes.erase(iter);
+ }
+ }
+
+private:
+ scope_list_t mActiveScopes;
+ Registrar mDefaultRegistrar;
+};
+
+template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistrySingleton
+ : public LLRegistry<KEY, VALUE, COMPARATOR>,
+ public LLSingleton<DERIVED_TYPE>
+{
+ friend class LLSingleton<DERIVED_TYPE>;
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef const KEY& ref_const_key_t;
+ typedef const VALUE& ref_const_value_t;
+ typedef VALUE* ptr_value_t;
+ typedef const VALUE* ptr_const_value_t;
+ typedef LLSingleton<DERIVED_TYPE> singleton_t;
+
+ class ScopedRegistrar : public registry_t::Registrar
+ {
+ public:
+ ScopedRegistrar(bool push_scope = true)
+ {
+ if (push_scope)
+ {
+ pushScope();
+ }
+ }
+
+ ~ScopedRegistrar()
+ {
+ if (!singleton_t::destroyed())
+ {
+ popScope();
+ }
+ }
+
+ void pushScope()
+ {
+ singleton_t::instance().addScope(this);
+ }
+
+ void popScope()
+ {
+ singleton_t::instance().removeScope(this);
+ }
+
+ ptr_value_t getValueFromScope(ref_const_key_t key)
+ {
+ return getValue(key);
+ }
+
+ ptr_const_value_t getValueFromScope(ref_const_key_t key) const
+ {
+ return getValue(key);
+ }
+
+ private:
+ typename std::list<typename registry_t::Registrar*>::iterator mListIt;
+ };
+
+ class StaticRegistrar : public registry_t::Registrar
+ {
+ public:
+ virtual ~StaticRegistrar() {}
+ StaticRegistrar(ref_const_key_t key, ref_const_value_t value)
+ {
+ if (singleton_t::instance().exists(key))
+ {
+ LL_ERRS() << "Duplicate registry entry under key \"" << key << "\"" << LL_ENDL;
+ }
+ singleton_t::instance().mStaticScope->add(key, value);
+ }
+ };
+
+ // convenience functions
+ typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t;
+ static ref_registrar_t currentRegistrar()
+ {
+ return singleton_t::instance().registry_t::currentRegistrar();
+ }
+
+ static ref_registrar_t defaultRegistrar()
+ {
+ return singleton_t::instance().registry_t::defaultRegistrar();
+ }
+
+ static ptr_value_t getValue(ref_const_key_t key)
+ {
+ return singleton_t::instance().registry_t::getValue(key);
+ }
+
+protected:
+ // DERIVED_TYPE needs to derive from LLRegistrySingleton
+ LLRegistrySingleton()
+ : mStaticScope(NULL)
+ {}
+
+ virtual void initSingleton()
+ {
+ mStaticScope = new ScopedRegistrar();
+ }
+
+ virtual ~LLRegistrySingleton()
+ {
+ delete mStaticScope;
+ }
+
+private:
+ ScopedRegistrar* mStaticScope;
+};
+
+// helper macro for doing static registration
+#define GLUED_TOKEN(x, y) x ## y
+#define GLUE_TOKENS(x, y) GLUED_TOKEN(x, y)
+#define LLREGISTER_STATIC(REGISTRY, KEY, VALUE) static REGISTRY::StaticRegistrar GLUE_TOKENS(reg, __LINE__)(KEY, VALUE);
+
+#endif
diff --git a/indra/llcommon/llrun.cpp b/indra/llcommon/llrun.cpp
index e2f69ff13d..f5d3f302fa 100644..100755
--- a/indra/llcommon/llrun.cpp
+++ b/indra/llcommon/llrun.cpp
@@ -4,31 +4,25 @@
* @date 2006-02-16
* @brief Implementation of the LLRunner and related classes
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h
index 1fc9925df9..a117405366 100644..100755
--- a/indra/llcommon/llrun.h
+++ b/indra/llcommon/llrun.h
@@ -4,31 +4,25 @@
* @date 2006-02-16
* @brief Declaration of LLRunner and LLRunnable classes.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h
index 5862f1a341..4226bf04f0 100644..100755
--- a/indra/llcommon/llsafehandle.h
+++ b/indra/llcommon/llsafehandle.h
@@ -2,31 +2,25 @@
* @file llsafehandle.h
* @brief Reference-counted object where Object() is valid, not NULL.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLSAFEHANDLE_H
@@ -140,7 +134,7 @@ protected:
tempp->unref();
if (mPointer != NULL)
{
- llwarns << "Unreference did assignment to non-NULL because of destructor" << llendl;
+ LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL;
unref();
}
}
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
index c863d4e266..57aa7d9c07 100644..100755
--- a/indra/llcommon/llsd.cpp
+++ b/indra/llcommon/llsd.cpp
@@ -2,34 +2,31 @@
* @file llsd.cpp
* @brief LLSD flexible data system
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
+// Must turn on conditional declarations in header file so definitions end up
+// with proper linkage.
+#define LLSD_DEBUG_INFO
#include "linden_common.h"
#include "llsd.h"
@@ -37,6 +34,7 @@
#include "../llmath/llmath.h"
#include "llformat.h"
#include "llsdserialize.h"
+#include "stringize.h"
#ifndef LL_RELEASE_FOR_DOWNLOAD
#define NAME_UNNAMED_NAMESPACE
@@ -56,6 +54,18 @@ namespace
using namespace LLSDUnnamedNamespace;
#endif
+namespace llsd
+{
+
+// statics
+S32 sLLSDAllocationCount = 0;
+S32 sLLSDNetObjects = 0;
+
+} // namespace llsd
+
+#define ALLOC_LLSD_OBJECT { llsd::sLLSDNetObjects++; llsd::sLLSDAllocationCount++; }
+#define FREE_LLSD_OBJECT { llsd::sLLSDNetObjects--; }
+
class LLSD::Impl
/**< This class is the abstract base class of the implementation of LLSD
It provides the reference counting implementation, and the default
@@ -64,13 +74,10 @@ class LLSD::Impl
*/
{
-private:
- U32 mUseCount;
-
protected:
Impl();
- enum StaticAllocationMarker { STATIC };
+ enum StaticAllocationMarker { STATIC_USAGE_COUNT = 0xFFFFFFFF };
Impl(StaticAllocationMarker);
///< This constructor is used for static objects and causes the
// suppresses adjusting the debugging counters when they are
@@ -78,8 +85,10 @@ protected:
virtual ~Impl();
- bool shared() const { return mUseCount > 1; }
+ bool shared() const { return (mUseCount > 1) && (mUseCount != STATIC_USAGE_COUNT); }
+ U32 mUseCount;
+
public:
static void reset(Impl*& var, Impl* impl);
///< safely set var to refer to the new impl (possibly shared)
@@ -117,7 +126,9 @@ public:
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 const Binary& asBinary() const { static const std::vector<U8> empty; return empty; }
+
+ virtual const String& asStringRef() const { static const std::string empty; return empty; }
virtual bool has(const String&) const { return false; }
virtual LLSD get(const String&) const { return LLSD(); }
@@ -134,6 +145,18 @@ public:
virtual LLSD::array_const_iterator beginArray() const { return endArray(); }
virtual LLSD::array_const_iterator endArray() const { static const std::vector<LLSD> empty; return empty.end(); }
+ virtual void dumpStats() const;
+ virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
+ // Container subclasses contain LLSD objects, rather than directly
+ // containing Impl objects. This helper forwards through LLSD.
+ void calcStats(const LLSD& llsd, S32 type_counts[], S32 share_counts[]) const
+ {
+ safe(llsd.impl).calcStats(type_counts, share_counts);
+ }
+
+ static const Impl& getImpl(const LLSD& llsd) { return safe(llsd.impl); }
+ static Impl& getImpl(LLSD& llsd) { return safe(llsd.impl); }
+
static const LLSD& undef();
static U32 sAllocationCount;
@@ -248,6 +271,8 @@ namespace
virtual LLSD::UUID asUUID() const { return LLUUID(mValue); }
virtual LLSD::Date asDate() const { return LLDate(mValue); }
virtual LLSD::URI asURI() const { return LLURI(mValue); }
+ virtual int size() const { return mValue.size(); }
+ virtual const LLSD::String& asStringRef() const { return mValue; }
};
LLSD::Integer ImplString::asInteger() const
@@ -326,7 +351,7 @@ namespace
public:
ImplBinary(const LLSD::Binary& v) : Base(v) { }
- virtual LLSD::Binary asBinary() const{ return mValue; }
+ virtual const LLSD::Binary& asBinary() const{ return mValue; }
};
@@ -366,6 +391,9 @@ namespace
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(); }
+
+ virtual void dumpStats() const;
+ virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
};
ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
@@ -420,6 +448,34 @@ namespace
return i->second;
}
+ void ImplMap::dumpStats() const
+ {
+ std::cout << "Map size: " << mData.size() << std::endl;
+
+ std::cout << "LLSD Net Objects: " << llsd::sLLSDNetObjects << std::endl;
+ std::cout << "LLSD allocations: " << llsd::sLLSDAllocationCount << std::endl;
+
+ std::cout << "LLSD::Impl Net Objects: " << sOutstandingCount << std::endl;
+ std::cout << "LLSD::Impl allocations: " << sAllocationCount << std::endl;
+
+ Impl::dumpStats();
+ }
+
+ void ImplMap::calcStats(S32 type_counts[], S32 share_counts[]) const
+ {
+ LLSD::map_const_iterator iter = beginMap();
+ while (iter != endMap())
+ {
+ //std::cout << " " << (*iter).first << ": " << (*iter).second << std::endl;
+ Impl::calcStats((*iter).second, type_counts, share_counts);
+ iter++;
+ }
+
+ // Add in the values for this map
+ Impl::calcStats(type_counts, share_counts);
+ }
+
+
class ImplArray : public LLSD::Impl
{
private:
@@ -453,8 +509,12 @@ namespace
LLSD::array_iterator beginArray() { return mData.begin(); }
LLSD::array_iterator endArray() { return mData.end(); }
+ LLSD::reverse_array_iterator rbeginArray() { return mData.rbegin(); }
+ LLSD::reverse_array_iterator rendArray() { return mData.rend(); }
virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); }
virtual LLSD::array_const_iterator endArray() const { return mData.end(); }
+
+ virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
};
ImplArray& ImplArray::makeArray(Impl*& var)
@@ -496,12 +556,13 @@ namespace
void ImplArray::insert(LLSD::Integer i, const LLSD& v)
{
- if (i < 0) {
+ if (i < 0)
+ {
return;
}
DataVector::size_type index = i;
- if (index >= mData.size())
+ if (index >= mData.size()) // tbd - sanity check limit for index ?
{
mData.resize(index + 1);
}
@@ -549,6 +610,19 @@ namespace
return mData[index];
}
+
+ void ImplArray::calcStats(S32 type_counts[], S32 share_counts[]) const
+ {
+ LLSD::array_const_iterator iter = beginArray();
+ while (iter != endArray())
+ { // Add values for all items held in the array
+ Impl::calcStats((*iter), type_counts, share_counts);
+ iter++;
+ }
+
+ // Add in the values for this array
+ Impl::calcStats(type_counts, share_counts);
+ }
}
LLSD::Impl::Impl()
@@ -570,8 +644,11 @@ LLSD::Impl::~Impl()
void LLSD::Impl::reset(Impl*& var, Impl* impl)
{
- if (impl) ++impl->mUseCount;
- if (var && --var->mUseCount == 0)
+ if (impl && impl->mUseCount != STATIC_USAGE_COUNT)
+ {
+ ++impl->mUseCount;
+ }
+ if (var && var->mUseCount != STATIC_USAGE_COUNT && --var->mUseCount == 0)
{
delete var;
}
@@ -580,13 +657,13 @@ void LLSD::Impl::reset(Impl*& var, Impl* impl)
LLSD::Impl& LLSD::Impl::safe(Impl* impl)
{
- static Impl theUndefined(STATIC);
+ static Impl theUndefined(STATIC_USAGE_COUNT);
return impl ? *impl : theUndefined;
}
const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)
{
- static Impl theUndefined(STATIC);
+ static Impl theUndefined(STATIC_USAGE_COUNT);
return impl ? *impl : theUndefined;
}
@@ -662,6 +739,43 @@ const LLSD& LLSD::Impl::undef()
return immutableUndefined;
}
+void LLSD::Impl::dumpStats() const
+{
+ S32 type_counts[LLSD::TypeLLSDNumTypes + 1];
+ memset(&type_counts, 0, sizeof(type_counts));
+
+ S32 share_counts[LLSD::TypeLLSDNumTypes + 1];
+ memset(&share_counts, 0, sizeof(share_counts));
+
+ // Add info from all the values this object has
+ calcStats(type_counts, share_counts);
+
+ S32 type_index = LLSD::TypeLLSDTypeBegin;
+ while (type_index != LLSD::TypeLLSDTypeEnd)
+ {
+ std::cout << LLSD::typeString((LLSD::Type)type_index) << " type "
+ << type_counts[type_index] << " objects, "
+ << share_counts[type_index] << " shared"
+ << std::endl;
+ type_index++;
+ }
+}
+
+
+void LLSD::Impl::calcStats(S32 type_counts[], S32 share_counts[]) const
+{
+ S32 tp = S32(type());
+ if (0 <= tp && tp < LLSD::TypeLLSDNumTypes)
+ {
+ type_counts[tp]++;
+ if (shared())
+ {
+ share_counts[tp]++;
+ }
+ }
+}
+
+
U32 LLSD::Impl::sAllocationCount = 0;
U32 LLSD::Impl::sOutstandingCount = 0;
@@ -675,10 +789,7 @@ 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); }
@@ -687,10 +798,10 @@ namespace
}
-LLSD::LLSD() : impl(0) { }
-LLSD::~LLSD() { Impl::reset(impl, 0); }
+LLSD::LLSD() : impl(0) { ALLOC_LLSD_OBJECT; }
+LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, 0); }
-LLSD::LLSD(const LLSD& other) : impl(0) { assign(other); }
+LLSD::LLSD(const LLSD& other) : impl(0) { ALLOC_LLSD_OBJECT; assign(other); }
void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); }
@@ -698,18 +809,18 @@ 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); }
+// Scalar Constructors
+LLSD::LLSD(Boolean v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(Integer v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(Real v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const UUID& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const String& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const Date& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const URI& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
+LLSD::LLSD(const Binary& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
// Convenience Constructors
-LLSD::LLSD(F32 v) : impl(0) { assign((Real)v); }
+LLSD::LLSD(F32 v) : impl(0) { ALLOC_LLSD_OBJECT; assign((Real)v); }
// Scalar Assignment
void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); }
@@ -729,10 +840,12 @@ 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 LLSD::Binary& LLSD::asBinary() const { return safe(impl).asBinary(); }
+
+const LLSD::String& LLSD::asStringRef() const { return safe(impl).asStringRef(); }
// const char * helpers
-LLSD::LLSD(const char* v) : impl(0) { assign(v); }
+LLSD::LLSD(const char* v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
void LLSD::assign(const char* v)
{
if(v) assign(std::string(v));
@@ -790,9 +903,6 @@ LLSD& LLSD::operator[](Integer 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; }
-
static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
{
// sStorage is used to hold the string representation of the llsd last
@@ -807,15 +917,9 @@ static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
{
std::ostringstream out;
if (useXMLFormat)
- {
- LLSDXMLStreamer xml_streamer(llsd);
- out << xml_streamer;
- }
+ out << LLSDXMLStreamer(llsd);
else
- {
- LLSDNotationStreamer notation_streamer(llsd);
- out << notation_streamer;
- }
+ out << LLSDNotationStreamer(llsd);
out_string = out.str();
}
int len = out_string.length();
@@ -846,3 +950,41 @@ 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(); }
+
+LLSD::reverse_array_iterator LLSD::rbeginArray() { return makeArray(impl).rbeginArray(); }
+LLSD::reverse_array_iterator LLSD::rendArray() { return makeArray(impl).rendArray(); }
+
+namespace llsd
+{
+
+U32 allocationCount() { return LLSD::Impl::sAllocationCount; }
+U32 outstandingCount() { return LLSD::Impl::sOutstandingCount; }
+
+// Diagnostic dump of contents in an LLSD object
+void dumpStats(const LLSD& llsd) { LLSD::Impl::getImpl(llsd).dumpStats(); }
+
+} // namespace llsd
+
+// static
+std::string LLSD::typeString(Type type)
+{
+ static const char * sTypeNameArray[] = {
+ "Undefined",
+ "Boolean",
+ "Integer",
+ "Real",
+ "String",
+ "UUID",
+ "Date",
+ "URI",
+ "Binary",
+ "Map",
+ "Array"
+ };
+
+ if (0 <= type && type < LL_ARRAY_SIZE(sTypeNameArray))
+ {
+ return sTypeNameArray[type];
+ }
+ return STRINGIZE("** invalid type value " << type);
+}
diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h
index 135133c19c..7b9b1285f5 100644..100755
--- a/indra/llcommon/llsd.h
+++ b/indra/llcommon/llsd.h
@@ -2,31 +2,25 @@
* @file llsd.h
* @brief LLSD flexible data system.
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -46,10 +40,10 @@
/**
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"
+ of structured data between loosely coupled systems. (Here, "loosely coupled"
means not compiled together into the same module.)
- Data in such exchanges must be highly tollerant of changes on either side
+ Data in such exchanges must be highly tolerant of changes on either side
such as:
- recompilation
- implementation in a different langauge
@@ -57,19 +51,19 @@
- 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
+ default to "the right thing" wherever possible. It is extremely tolerant
of errors and unexpected situations.
- The fundimental class is LLSD. LLSD is a value holding object. It holds
+ The fundamental 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.
+ though it can be considered efficient, due to sharing), 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,
+ operations that return an LLSD.
- The sclar data types are:
+ The scalar data types are:
- Boolean - true or false
- Integer - a 32 bit signed integer
- Real - a 64 IEEE 754 floating point value
@@ -86,9 +80,73 @@
An array is a sequence of zero or more LLSD values.
+ Thread Safety
+
+ In general, these LLSD classes offer *less* safety than STL container
+ classes. Implementations prior to this one were unsafe even when
+ completely unrelated LLSD trees were in two threads due to reference
+ sharing of special 'undefined' values that participated in the reference
+ counting mechanism.
+
+ The dereference-before-refcount and aggressive tree sharing also make
+ it impractical to share an LLSD across threads. A strategy of passing
+ ownership or a copy to another thread is still difficult due to a lack
+ of a cloning interface but it can be done with some care.
+
+ One way of transferring ownership is as follows:
+
+ void method(const LLSD input) {
+ ...
+ LLSD * xfer_tree = new LLSD();
+ {
+ // Top-level values
+ (* xfer_tree)['label'] = "Some text";
+ (* xfer_tree)['mode'] = APP_MODE_CONSTANT;
+
+ // There will be a second-level
+ LLSD subtree(LLSD::emptyMap());
+ (* xfer_tree)['subtree'] = subtree;
+
+ // Do *not* copy from LLSD objects via LLSD
+ // intermediaries. Only use plain-old-data
+ // types as intermediaries to prevent reference
+ // sharing.
+ subtree['value1'] = input['value1'].asInteger();
+ subtree['value2'] = input['value2'].asString();
+
+ // Close scope and drop 'subtree's reference.
+ // Only xfer_tree has a reference to the second
+ // level data.
+ }
+ ...
+ // Transfer the LLSD pointer to another thread. Ownership
+ // transfers, this thread no longer has a reference to any
+ // part of the xfer_tree and there's nothing to free or
+ // release here. Receiving thread does need to delete the
+ // pointer when it is done with the LLSD. Transfer
+ // mechanism must perform correct data ordering operations
+ // as dictated by architecture.
+ other_thread.sendMessageAndPointer("Take This", xfer_tree);
+ xfer_tree = NULL;
+
+
+ Avoid this pattern which provides half of a race condition:
+
+ void method(const LLSD input) {
+ ...
+ LLSD xfer_tree(LLSD::emptyMap());
+ xfer_tree['label'] = "Some text";
+ xfer_tree['mode'] = APP_MODE_CONSTANT;
+ ...
+ other_thread.sendMessageAndPointer("Take This", xfer_tree);
+
+
@nosubgrouping
*/
+// Normally undefined, used for diagnostics
+//#define LLSD_DEBUG_INFO 1
+
class LL_COMMON_API LLSD
{
public:
@@ -191,7 +249,10 @@ public:
UUID asUUID() const;
Date asDate() const;
URI asURI() const;
- Binary asBinary() const;
+ const Binary& asBinary() const;
+
+ // asStringRef on any non-string type will return a ref to an empty string.
+ const String& asStringRef() const;
operator Boolean() const { return asBoolean(); }
operator Integer() const { return asInteger(); }
@@ -208,7 +269,7 @@ public:
//@}
/** @name Character Pointer Helpers
- These are helper routines to make working with char* the same as easy as
+ These are helper routines to make working with char* as easy as
working with strings.
*/
//@{
@@ -262,17 +323,21 @@ public:
typedef std::vector<LLSD>::iterator array_iterator;
typedef std::vector<LLSD>::const_iterator array_const_iterator;
+ typedef std::vector<LLSD>::reverse_iterator reverse_array_iterator;
array_iterator beginArray();
array_iterator endArray();
array_const_iterator beginArray() const;
array_const_iterator endArray() const;
+
+ reverse_array_iterator rbeginArray();
+ reverse_array_iterator rendArray();
//@}
/** @name Type Testing */
//@{
enum Type {
- TypeUndefined,
+ TypeUndefined = 0,
TypeBoolean,
TypeInteger,
TypeReal,
@@ -282,7 +347,10 @@ public:
TypeURI,
TypeBinary,
TypeMap,
- TypeArray
+ TypeArray,
+ TypeLLSDTypeEnd,
+ TypeLLSDTypeBegin = TypeUndefined,
+ TypeLLSDNumTypes = (TypeLLSDTypeEnd - TypeLLSDTypeBegin)
};
Type type() const;
@@ -308,7 +376,7 @@ public:
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
+ All of these 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.
*/
@@ -317,7 +385,7 @@ public:
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
+ bool has(Integer) const; ///< has() only works for Maps
//@}
/** @name Implementation */
@@ -326,13 +394,7 @@ 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
+ friend class LLSD::Impl;
//@}
private:
@@ -344,6 +406,10 @@ private:
/// Returns Notation version of llsd -- only to be called from debugger
static const char *dump(const LLSD &llsd);
//@}
+
+public:
+
+ static std::string typeString(Type type); // Return human-readable type as a string
};
struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean>
@@ -391,9 +457,32 @@ struct llsd_select_string : public std::unary_function<LLSD, LLSD::String>
LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd);
+namespace llsd
+{
+
+#ifdef LLSD_DEBUG_INFO
+/** @name Unit Testing Interface */
+//@{
+ LL_COMMON_API void dumpStats(const LLSD&); ///< Output information on object and usage
+
+ /// @warn THE FOLLOWING COUNTS WILL NOT BE ACCURATE IN A MULTI-THREADED
+ /// ENVIRONMENT.
+ ///
+ /// These counts track LLSD::Impl (hidden) objects.
+ LL_COMMON_API U32 allocationCount(); ///< how many Impls have been made
+ LL_COMMON_API U32 outstandingCount(); ///< how many Impls are still alive
+
+ /// These counts track LLSD (public) objects.
+ LL_COMMON_API extern S32 sLLSDAllocationCount; ///< Number of LLSD objects ever created
+ LL_COMMON_API extern S32 sLLSDNetObjects; ///< Number of LLSD objects that exist
+#endif
+//@}
+
+} // namespace llsd
+
/** QUESTIONS & TO DOS
- - Would Binary be more convenient as usigned char* buffer semantics?
- - Should Binary be convertable to/from String, and if so how?
+ - Would Binary be more convenient as unsigned char* buffer semantics?
+ - Should Binary be convertible 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
diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp
new file mode 100755
index 0000000000..2e7b46f885
--- /dev/null
+++ b/indra/llcommon/llsdparam.cpp
@@ -0,0 +1,344 @@
+/**
+ * @file llsdparam.cpp
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+// Project includes
+#include "llsdparam.h"
+#include "llsdutil.h"
+#include "boost/bind.hpp"
+
+static LLInitParam::Parser::parser_read_func_map_t sReadFuncs;
+static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs;
+static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs;
+static const LLSD NO_VALUE_MARKER;
+
+LLTrace::BlockTimerStatHandle FTM_SD_PARAM_ADAPTOR("LLSD to LLInitParam conversion");
+
+//
+// LLParamSDParser
+//
+LLParamSDParser::LLParamSDParser()
+: Parser(sReadFuncs, sWriteFuncs, sInspectFuncs)
+{
+ using boost::bind;
+
+ if (sReadFuncs.empty())
+ {
+ registerParserFuncs<LLInitParam::Flag>(readFlag, &LLParamSDParser::writeFlag);
+ registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>);
+ registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param);
+ registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>);
+ registerParserFuncs<F64>(readF64, &LLParamSDParser::writeTypedValue<F64>);
+ registerParserFuncs<bool>(readBool, &LLParamSDParser::writeTypedValue<bool>);
+ registerParserFuncs<std::string>(readString, &LLParamSDParser::writeTypedValue<std::string>);
+ registerParserFuncs<LLUUID>(readUUID, &LLParamSDParser::writeTypedValue<LLUUID>);
+ registerParserFuncs<LLDate>(readDate, &LLParamSDParser::writeTypedValue<LLDate>);
+ registerParserFuncs<LLURI>(readURI, &LLParamSDParser::writeTypedValue<LLURI>);
+ registerParserFuncs<LLSD>(readSD, &LLParamSDParser::writeTypedValue<LLSD>);
+ }
+}
+
+// special case handling of U32 due to ambiguous LLSD::assign overload
+bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack)
+{
+ LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
+ if (!sdparser.mWriteRootSD) return false;
+
+ parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end());
+ LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range);
+ sd_to_write.assign((S32)*((const U32*)val_ptr));
+
+ return true;
+}
+
+bool LLParamSDParser::writeFlag(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack)
+{
+ LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
+ if (!sdparser.mWriteRootSD) return false;
+
+ parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end());
+ LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range);
+
+ return true;
+}
+
+void LLParamSDParser::submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack)
+{
+ mCurReadSD = &sd;
+ block.submitValue(name_stack, *this);
+}
+
+void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent)
+{
+ mCurReadSD = NULL;
+ mNameStack.clear();
+ setParseSilently(silent);
+
+ LLParamSDParserUtilities::readSDValues(boost::bind(&LLParamSDParser::submit, this, boost::ref(block), _1, _2), sd, mNameStack);
+ //readSDValues(sd, block);
+}
+
+void LLParamSDParser::writeSDImpl(LLSD& sd, const LLInitParam::BaseBlock& block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block)
+{
+ mNameStack.clear();
+ mWriteRootSD = &sd;
+
+ name_stack_t name_stack;
+ block.serializeBlock(*this, name_stack, rules, diff_block);
+}
+
+/*virtual*/ std::string LLParamSDParser::getCurrentElementName()
+{
+ std::string full_name = "sd";
+ for (name_stack_t::iterator it = mNameStack.begin();
+ it != mNameStack.end();
+ ++it)
+ {
+ full_name += llformat("[%s]", it->first.c_str());
+ }
+
+ return full_name;
+}
+
+
+bool LLParamSDParser::readFlag(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+ return self.mCurReadSD == &NO_VALUE_MARKER;
+}
+
+
+bool LLParamSDParser::readS32(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((S32*)val_ptr) = self.mCurReadSD->asInteger();
+ return true;
+}
+
+bool LLParamSDParser::readU32(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((U32*)val_ptr) = self.mCurReadSD->asInteger();
+ return true;
+}
+
+bool LLParamSDParser::readF32(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((F32*)val_ptr) = self.mCurReadSD->asReal();
+ return true;
+}
+
+bool LLParamSDParser::readF64(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((F64*)val_ptr) = self.mCurReadSD->asReal();
+ return true;
+}
+
+bool LLParamSDParser::readBool(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((bool*)val_ptr) = self.mCurReadSD->asBoolean();
+ return true;
+}
+
+bool LLParamSDParser::readString(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((std::string*)val_ptr) = self.mCurReadSD->asString();
+ return true;
+}
+
+bool LLParamSDParser::readUUID(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((LLUUID*)val_ptr) = self.mCurReadSD->asUUID();
+ return true;
+}
+
+bool LLParamSDParser::readDate(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((LLDate*)val_ptr) = self.mCurReadSD->asDate();
+ return true;
+}
+
+bool LLParamSDParser::readURI(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((LLURI*)val_ptr) = self.mCurReadSD->asURI();
+ return true;
+}
+
+bool LLParamSDParser::readSD(Parser& parser, void* val_ptr)
+{
+ LLParamSDParser& self = static_cast<LLParamSDParser&>(parser);
+
+ *((LLSD*)val_ptr) = *self.mCurReadSD;
+ return true;
+}
+
+// static
+LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range)
+{
+ LLSD* sd_to_write = &input;
+
+ for (LLInitParam::Parser::name_stack_t::iterator it = name_stack_range.first;
+ it != name_stack_range.second;
+ ++it)
+ {
+ bool new_traversal = it->second;
+
+ LLSD* child_sd;
+ if (it->first.empty())
+ {
+ child_sd = sd_to_write;
+ if (child_sd->isUndefined())
+ {
+ *child_sd = LLSD::emptyArray();
+ }
+ if (new_traversal)
+ {
+ // write to new element at end
+ sd_to_write = &(*child_sd)[child_sd->size()];
+ }
+ else
+ {
+ // write to last of existing elements, or first element if empty
+ sd_to_write = &(*child_sd)[llmax(0, child_sd->size() - 1)];
+ }
+ }
+ else
+ {
+ sd_to_write = &(*sd_to_write)[it->first];
+ }
+ it->second = false;
+ }
+
+ return *sd_to_write;
+}
+
+//static
+void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack)
+{
+ if (sd.isMap())
+ {
+ for (LLSD::map_const_iterator it = sd.beginMap();
+ it != sd.endMap();
+ ++it)
+ {
+ stack.push_back(make_pair(it->first, true));
+ readSDValues(cb, it->second, stack);
+ stack.pop_back();
+ }
+ }
+ else if (sd.isArray())
+ {
+ for (LLSD::array_const_iterator it = sd.beginArray();
+ it != sd.endArray();
+ ++it)
+ {
+ stack.push_back(make_pair(std::string(), true));
+ readSDValues(cb, *it, stack);
+ stack.pop_back();
+ }
+ }
+ else if (sd.isUndefined())
+ {
+ if (!cb.empty())
+ {
+ cb(NO_VALUE_MARKER, stack);
+ }
+ }
+ else
+ {
+ if (!cb.empty())
+ {
+ cb(sd, stack);
+ }
+ }
+}
+
+//static
+void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd)
+{
+ LLInitParam::Parser::name_stack_t stack = LLInitParam::Parser::name_stack_t();
+ readSDValues(cb, sd, stack);
+}
+
+namespace LLInitParam
+{
+ // LLSD specialization
+ // block param interface
+ bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack, bool new_name)
+ {
+ if (name_stack.first == name_stack.second
+ && p.readValue<LLSD>(mValue))
+ {
+ return true;
+ }
+
+ LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack);
+
+ LLSD::String string;
+
+ if (p.readValue<LLSD::String>(string))
+ {
+ sd = string;
+ return true;
+ }
+ return false;
+ }
+
+ //static
+ void ParamValue<LLSD, NOT_BLOCK>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack)
+ {
+ p.writeValue<LLSD::String>(sd.asString(), name_stack);
+ }
+
+ bool ParamValue<LLSD, NOT_BLOCK>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack_range, const predicate_rule_t predicate_rule, const BaseBlock* diff_block) const
+ {
+ // attempt to write LLSD out directly
+ if (!p.writeValue<LLSD>(mValue, name_stack_range))
+ {
+ // otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc)
+ LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack_range);
+ }
+ return true;
+ }
+}
diff --git a/indra/llcommon/llsdparam.h b/indra/llcommon/llsdparam.h
new file mode 100755
index 0000000000..09f1bdf1e3
--- /dev/null
+++ b/indra/llcommon/llsdparam.h
@@ -0,0 +1,143 @@
+/**
+ * @file llsdparam.h
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSDPARAM_H
+#define LL_LLSDPARAM_H
+
+#include "llinitparam.h"
+#include "boost/function.hpp"
+#include "llfasttimer.h"
+
+struct LL_COMMON_API LLParamSDParserUtilities
+{
+ static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range);
+
+ typedef boost::function<void (const LLSD&, LLInitParam::Parser::name_stack_t&)> read_sd_cb_t;
+ static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack);
+ static void readSDValues(read_sd_cb_t cb, const LLSD& sd);
+};
+
+class LL_COMMON_API LLParamSDParser
+: public LLInitParam::Parser
+{
+LOG_CLASS(LLParamSDParser);
+
+typedef LLInitParam::Parser parser_t;
+
+public:
+ LLParamSDParser();
+ void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false);
+ template<typename BLOCK>
+ void writeSD(LLSD& sd,
+ const BLOCK& block,
+ const LLInitParam::predicate_rule_t rules = LLInitParam::default_parse_rules(),
+ const LLInitParam::BaseBlock* diff_block = NULL)
+ {
+ if (!diff_block
+ && !rules.isAmbivalent(LLInitParam::HAS_DEFAULT_VALUE))
+ {
+ diff_block = &LLInitParam::defaultValue<BLOCK>();
+ }
+ writeSDImpl(sd, block, rules, diff_block);
+ }
+
+ /*virtual*/ std::string getCurrentElementName();
+
+private:
+ void writeSDImpl(LLSD& sd,
+ const LLInitParam::BaseBlock& block,
+ const LLInitParam::predicate_rule_t,
+ const LLInitParam::BaseBlock* diff_block);
+
+ void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack);
+
+ template<typename T>
+ static bool writeTypedValue(Parser& parser, const void* val_ptr, parser_t::name_stack_t& name_stack)
+ {
+ LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser);
+ if (!sdparser.mWriteRootSD) return false;
+
+ LLInitParam::Parser::name_stack_range_t range(name_stack.begin(), name_stack.end());
+ LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range);
+
+ sd_to_write.assign(*((const T*)val_ptr));
+ return true;
+ }
+
+ static bool writeU32Param(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack);
+ static bool writeFlag(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack);
+
+ static bool readFlag(Parser& parser, void* val_ptr);
+ static bool readS32(Parser& parser, void* val_ptr);
+ static bool readU32(Parser& parser, void* val_ptr);
+ static bool readF32(Parser& parser, void* val_ptr);
+ static bool readF64(Parser& parser, void* val_ptr);
+ static bool readBool(Parser& parser, void* val_ptr);
+ static bool readString(Parser& parser, void* val_ptr);
+ static bool readUUID(Parser& parser, void* val_ptr);
+ static bool readDate(Parser& parser, void* val_ptr);
+ static bool readURI(Parser& parser, void* val_ptr);
+ static bool readSD(Parser& parser, void* val_ptr);
+
+ Parser::name_stack_t mNameStack;
+ const LLSD* mCurReadSD;
+ LLSD* mWriteRootSD;
+};
+
+
+extern LL_COMMON_API LLTrace::BlockTimerStatHandle FTM_SD_PARAM_ADAPTOR;
+template<typename T>
+class LLSDParamAdapter : public T
+{
+public:
+ LLSDParamAdapter() {}
+ LLSDParamAdapter(const LLSD& sd)
+ {
+ LL_RECORD_BLOCK_TIME(FTM_SD_PARAM_ADAPTOR);
+ LLParamSDParser parser;
+ // don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it
+ bool parse_silently = true;
+ parser.readSD(sd, *this, parse_silently);
+ }
+
+ operator LLSD() const
+ {
+ LLParamSDParser parser;
+ LLSD sd;
+ parser.writeSD(sd, *this);
+ return sd;
+ }
+
+ LLSDParamAdapter(const T& val)
+ : T(val)
+ {
+ T::operator=(val);
+ }
+};
+
+#endif // LL_LLSDPARAM_H
+
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index cf337be161..d49ff0feb5 100644..100755
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -4,31 +4,25 @@
* @date 2006-03-05
* @brief Implementation of LLSD parsers and formatters
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -40,6 +34,12 @@
#include <iostream>
#include "apr_base64.h"
+#ifdef LL_USESYSTEMLIBS
+# include <zlib.h>
+#else
+# include "zlib/zlib.h" // for davep's dirty little zip functions
+#endif
+
#if !LL_WINDOWS
#include <netinet/in.h> // htonl & ntohl
#endif
@@ -55,6 +55,10 @@ static const char LEGACY_NON_HEADER[] = "<llsd>";
const std::string LLSD_BINARY_HEADER("LLSD/Binary");
const std::string LLSD_XML_HEADER("LLSD/XML");
+//used to deflate a gzipped asset (currently used for navmeshes)
+#define windowBits 15
+#define ENABLE_ZLIB_GZIP 32
+
/**
* LLSDSerialize
*/
@@ -77,7 +81,7 @@ void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize
break;
default:
- llwarns << "serialize request for unkown ELLSD_Serialize" << llendl;
+ LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL;
}
if (f.notNull())
@@ -110,7 +114,7 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
{
legacy_no_header = true;
- inbuf = str.gcount();
+ inbuf = (int)str.gcount();
}
else
{
@@ -165,7 +169,7 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
}
else
{
- llwarns << "deserialize request for unknown ELLSD_Serialize" << llendl;
+ LL_WARNS() << "deserialize request for unknown ELLSD_Serialize" << LL_ENDL;
}
if (p.notNull())
@@ -175,7 +179,7 @@ bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, S32 max_bytes)
}
fail:
- llwarns << "deserialize LLSD parse failure" << llendl;
+ LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
return false;
}
@@ -343,7 +347,7 @@ std::istream& LLSDParser::get(
char delim) const
{
istr.get(s, n, delim);
- if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount();
return istr;
}
@@ -353,7 +357,7 @@ std::istream& LLSDParser::get(
char delim) const
{
istr.get(sb, delim);
- if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount();
return istr;
}
@@ -377,7 +381,7 @@ std::istream& LLSDParser::read(
std::streamsize n) const
{
istr.read(s, n);
- if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ if(mCheckLimits) mMaxBytesLeft -= (int)istr.gcount();
return istr;
}
@@ -441,7 +445,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading map." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading map." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -460,7 +464,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading array." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading array." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -496,7 +500,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading boolean." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -522,7 +526,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading boolean." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -535,7 +539,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
data = integer;
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading integer." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading integer." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -549,7 +553,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
data = real;
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading real." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading real." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -563,7 +567,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
data = id;
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading uuid." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading uuid." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -578,7 +582,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading string." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading string." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -600,7 +604,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading link." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading link." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -623,7 +627,7 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading date." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading date." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -636,15 +640,15 @@ S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading data." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading data." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
default:
parse_count = PARSE_FAILURE;
- llinfos << "Unrecognized character while parsing: int(" << (int)c
- << ")" << llendl;
+ LL_INFOS() << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << LL_ENDL;
break;
}
if(PARSE_FAILURE == parse_count)
@@ -789,7 +793,7 @@ bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
if(len)
{
value.resize(len);
- account(fullread(istr, (char *)&value[0], len));
+ account((int)fullread(istr, (char *)&value[0], len));
}
c = get(istr); // strip off the trailing double-quote
data = value;
@@ -869,7 +873,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
{
/**
* Undefined: '!'<br>
- * Boolean: 't' for true 'f' for false<br>
+ * Boolean: '1' for true '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>
@@ -905,7 +909,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary map." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary map." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -924,7 +928,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary array." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary array." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -949,7 +953,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
data = (S32)ntohl(value_nbo);
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary integer." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary integer." << LL_ENDL;
}
break;
}
@@ -961,7 +965,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
data = ll_ntohd(real_nbo);
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary real." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary real." << LL_ENDL;
}
break;
}
@@ -973,7 +977,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
data = id;
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary uuid." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary uuid." << LL_ENDL;
}
break;
}
@@ -994,8 +998,8 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary (notation-style) string."
- << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary (notation-style) string."
+ << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -1014,7 +1018,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary string." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary string." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -1033,7 +1037,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary link." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary link." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -1046,7 +1050,7 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
data = LLDate(real);
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary date." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary date." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -1069,13 +1073,13 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
if(size > 0)
{
value.resize(size);
- account(fullread(istr, (char*)&value[0], size));
+ account((int)fullread(istr, (char*)&value[0], size));
}
data = value;
}
if(istr.fail())
{
- llinfos << "STREAM FAILURE reading binary." << llendl;
+ LL_INFOS() << "STREAM FAILURE reading binary." << LL_ENDL;
parse_count = PARSE_FAILURE;
}
break;
@@ -1083,8 +1087,8 @@ S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data) const
default:
parse_count = PARSE_FAILURE;
- llinfos << "Unrecognized character while parsing: int(" << (int)c
- << ")" << llendl;
+ LL_INFOS() << "Unrecognized character while parsing: int(" << (int)c
+ << ")" << LL_ENDL;
break;
}
if(PARSE_FAILURE == parse_count)
@@ -1200,7 +1204,7 @@ bool LLSDBinaryParser::parseString(
if(size)
{
buf.resize(size);
- account(fullread(istr, &buf[0], size));
+ account((int)fullread(istr, &buf[0], size));
value.assign(buf.begin(), buf.end());
}
return true;
@@ -1257,12 +1261,37 @@ std::string LLSDNotationFormatter::escapeString(const std::string& in)
// virtual
S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 options) const
{
+ S32 rv = format_impl(data, ostr, options, 0);
+ return rv;
+}
+
+S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 options, U32 level) const
+{
S32 format_count = 1;
+ std::string pre;
+ std::string post;
+
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ for (U32 i = 0; i < level; i++)
+ {
+ pre += " ";
+ }
+ post = "\n";
+ }
+
switch(data.type())
{
case LLSD::TypeMap:
{
+ if (0 != level) ostr << post << pre;
ostr << "{";
+ std::string inner_pre;
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ inner_pre = pre + " ";
+ }
+
bool need_comma = false;
LLSD::map_const_iterator iter = data.beginMap();
LLSD::map_const_iterator end = data.endMap();
@@ -1270,18 +1299,18 @@ S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 opti
{
if(need_comma) ostr << ",";
need_comma = true;
- ostr << '\'';
+ ostr << post << inner_pre << '\'';
serialize_string((*iter).first, ostr);
ostr << "':";
- format_count += format((*iter).second, ostr);
+ format_count += format_impl((*iter).second, ostr, options, level + 2);
}
- ostr << "}";
+ ostr << post << pre << "}";
break;
}
case LLSD::TypeArray:
{
- ostr << "[";
+ ostr << post << pre << "[";
bool need_comma = false;
LLSD::array_const_iterator iter = data.beginArray();
LLSD::array_const_iterator end = data.endArray();
@@ -1289,7 +1318,7 @@ S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 opti
{
if(need_comma) ostr << ",";
need_comma = true;
- format_count += format(*iter, ostr);
+ format_count += format_impl(*iter, ostr, options, level + 1);
}
ostr << "]";
break;
@@ -1339,7 +1368,7 @@ S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 opti
case LLSD::TypeString:
ostr << '\'';
- serialize_string(data.asString(), ostr);
+ serialize_string(data.asStringRef(), ostr);
ostr << '\'';
break;
@@ -1356,9 +1385,26 @@ S32 LLSDNotationFormatter::format(const LLSD& data, std::ostream& ostr, U32 opti
case LLSD::TypeBinary:
{
// *FIX: memory inefficient.
- std::vector<U8> buffer = data.asBinary();
+ const std::vector<U8>& buffer = data.asBinary();
ostr << "b(" << buffer.size() << ")\"";
- if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ if(buffer.size())
+ {
+ if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY)
+ {
+ std::ios_base::fmtflags old_flags = ostr.flags();
+ ostr.setf( std::ios::hex, std::ios::basefield );
+ ostr << "0x";
+ for (int i = 0; i < buffer.size(); i++)
+ {
+ ostr << (int) buffer[i];
+ }
+ ostr.flags(old_flags);
+ }
+ else
+ {
+ ostr.write((const char*)&buffer[0], buffer.size());
+ }
+ }
ostr << "\"";
break;
}
@@ -1447,13 +1493,16 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option
}
case LLSD::TypeUUID:
+ {
ostr.put('u');
- ostr.write((const char*)(&(data.asUUID().mData)), UUID_BYTES);
+ LLUUID temp = data.asUUID();
+ ostr.write((const char*)(&(temp.mData)), UUID_BYTES);
break;
+ }
case LLSD::TypeString:
ostr.put('s');
- formatString(data.asString(), ostr);
+ formatString(data.asStringRef(), ostr);
break;
case LLSD::TypeDate:
@@ -1471,9 +1520,8 @@ S32 LLSDBinaryFormatter::format(const LLSD& data, std::ostream& ostr, U32 option
case LLSD::TypeBinary:
{
- // *FIX: memory inefficient.
ostr.put('b');
- std::vector<U8> buffer = data.asBinary();
+ const 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());
@@ -1642,7 +1690,7 @@ int deserialize_string_raw(
const S32 BUF_LEN = 20;
char buf[BUF_LEN]; /* Flawfinder: ignore */
istr.get(buf, BUF_LEN - 1, ')');
- count += istr.gcount();
+ count += (int)istr.gcount();
int c = istr.get();
c = istr.get();
count += 2;
@@ -1657,7 +1705,7 @@ int deserialize_string_raw(
if(len)
{
buf.resize(len);
- count += fullread(istr, (char *)&buf[0], len);
+ count += (int)fullread(istr, (char *)&buf[0], len);
value.assign(buf.begin(), buf.end());
}
c = istr.get();
@@ -1989,3 +2037,257 @@ std::ostream& operator<<(std::ostream& s, const LLSD& llsd)
return s;
}
+
+//dirty little zippers -- yell at davep if these are horrid
+
+//return a string containing gzipped bytes of binary serialized LLSD
+// VERY inefficient -- creates several copies of LLSD block in memory
+std::string zip_llsd(LLSD& data)
+{
+ std::stringstream llsd_strm;
+
+ LLSDSerialize::toBinary(data, llsd_strm);
+
+ const U32 CHUNK = 65536;
+
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION);
+ if (ret != Z_OK)
+ {
+ LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
+ return std::string();
+ }
+
+ std::string source = llsd_strm.str();
+
+ U8 out[CHUNK];
+
+ strm.avail_in = source.size();
+ strm.next_in = (U8*) source.data();
+ U8* output = NULL;
+
+ U32 cur_size = 0;
+
+ U32 have = 0;
+
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret == Z_OK || ret == Z_STREAM_END)
+ { //copy result into output
+ if (strm.avail_out >= CHUNK)
+ {
+ free(output);
+ LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
+ return std::string();
+ }
+
+ have = CHUNK-strm.avail_out;
+ output = (U8*) realloc(output, cur_size+have);
+ memcpy(output+cur_size, out, have);
+ cur_size += have;
+ }
+ else
+ {
+ free(output);
+ LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
+ return std::string();
+ }
+ }
+ while (ret == Z_OK);
+
+ std::string::size_type size = cur_size;
+
+ std::string result((char*) output, size);
+ deflateEnd(&strm);
+ free(output);
+
+#if 0 //verify results work with unzip_llsd
+ std::istringstream test(result);
+ LLSD test_sd;
+ if (!unzip_llsd(test_sd, test, result.size()))
+ {
+ LL_ERRS() << "Invalid compression result!" << LL_ENDL;
+ }
+#endif
+
+ return result;
+}
+
+//decompress a block of LLSD from provided istream
+// not very efficient -- creats a copy of decompressed LLSD block in memory
+// and deserializes from that copy using LLSDSerialize
+bool unzip_llsd(LLSD& data, std::istream& is, S32 size)
+{
+ U8* result = NULL;
+ U32 cur_size = 0;
+ z_stream strm;
+
+ const U32 CHUNK = 65536;
+
+ U8 *in = new U8[size];
+ is.read((char*) in, size);
+
+ U8 out[CHUNK];
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = size;
+ strm.next_in = in;
+
+ S32 ret = inflateInit(&strm);
+
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR)
+ {
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ return false;
+ }
+
+ switch (ret)
+ {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ return false;
+ break;
+ }
+
+ U32 have = CHUNK-strm.avail_out;
+
+ result = (U8*) realloc(result, cur_size + have);
+ memcpy(result+cur_size, out, have);
+ cur_size += have;
+
+ } while (ret == Z_OK);
+
+ inflateEnd(&strm);
+ delete [] in;
+
+ if (ret != Z_STREAM_END)
+ {
+ free(result);
+ return false;
+ }
+
+ //result now points to the decompressed LLSD block
+ {
+ std::string res_str((char*) result, cur_size);
+
+ std::string deprecated_header("<? LLSD/Binary ?>");
+
+ if (res_str.substr(0, deprecated_header.size()) == deprecated_header)
+ {
+ res_str = res_str.substr(deprecated_header.size()+1, cur_size);
+ }
+ cur_size = res_str.size();
+
+ std::istringstream istr(res_str);
+
+ if (!LLSDSerialize::fromBinary(data, istr, cur_size))
+ {
+ LL_WARNS() << "Failed to unzip LLSD block" << LL_ENDL;
+ free(result);
+ return false;
+ }
+ }
+
+ free(result);
+ return true;
+}
+//This unzip function will only work with a gzip header and trailer - while the contents
+//of the actual compressed data is the same for either format (gzip vs zlib ), the headers
+//and trailers are different for the formats.
+U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize, std::istream& is, S32 size )
+{
+ U8* result = NULL;
+ U32 cur_size = 0;
+ z_stream strm;
+
+ const U32 CHUNK = 0x4000;
+
+ U8 *in = new U8[size];
+ is.read((char*) in, size);
+
+ U8 out[CHUNK];
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = size;
+ strm.next_in = in;
+
+
+ S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP );
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR)
+ {
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ valid = false;
+ }
+
+ switch (ret)
+ {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ valid = false;
+ break;
+ }
+
+ U32 have = CHUNK-strm.avail_out;
+
+ result = (U8*) realloc(result, cur_size + have);
+ memcpy(result+cur_size, out, have);
+ cur_size += have;
+
+ } while (ret == Z_OK);
+
+ inflateEnd(&strm);
+ delete [] in;
+
+ if (ret != Z_STREAM_END)
+ {
+ free(result);
+ valid = false;
+ return NULL;
+ }
+
+ //result now points to the decompressed LLSD block
+ {
+ outsize= cur_size;
+ valid = true;
+ }
+
+ return result;
+}
+
+
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
index 2f2b292189..23a0c8cfb1 100644..100755
--- a/indra/llcommon/llsdserialize.h
+++ b/indra/llcommon/llsdserialize.h
@@ -4,31 +4,25 @@
* @date 2006-02-26
* @brief Declaration of parsers and formatters for LLSD
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -306,7 +300,7 @@ public:
/**
* @brief Constructor
*/
- LLSDXMLParser();
+ LLSDXMLParser(bool emit_errors=true);
protected:
/**
@@ -422,7 +416,8 @@ public:
typedef enum e_formatter_options_type
{
OPTIONS_NONE = 0,
- OPTIONS_PRETTY = 1
+ OPTIONS_PRETTY = 1,
+ OPTIONS_PRETTY_BINARY = 2
} EFormatterOptions;
/**
@@ -513,6 +508,17 @@ public:
* @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;
};
@@ -640,7 +646,7 @@ protected:
* </code>
*
* *NOTE - formerly this class inherited from its template parameter Formatter,
- * but all insnatiations passed in LLRefCount subclasses. This conflicted with
+ * but all instantiations passed in LLRefCount subclasses. This conflicted with
* the auto allocation intended for this class template (demonstrated in the
* example above). -brad
*/
@@ -726,6 +732,18 @@ public:
LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter;
return f->format(sd, str, LLSDFormatter::OPTIONS_NONE);
}
+ static S32 toPrettyNotation(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter;
+ return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY);
+ }
+ static S32 toPrettyBinaryNotation(const LLSD& sd, std::ostream& str)
+ {
+ LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter;
+ return f->format(sd, str,
+ LLSDFormatter::OPTIONS_PRETTY |
+ LLSDFormatter::OPTIONS_PRETTY_BINARY);
+ }
static S32 fromNotation(LLSD& sd, std::istream& str, S32 max_bytes)
{
LLPointer<LLSDNotationParser> p = new LLSDNotationParser;
@@ -753,25 +771,25 @@ public:
return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY);
}
- static S32 fromXMLEmbedded(LLSD& sd, std::istream& str)
+ static S32 fromXMLEmbedded(LLSD& sd, std::istream& str, bool emit_errors=true)
{
// no need for max_bytes since xml formatting is not
// subvertable by bad sizes.
- LLPointer<LLSDXMLParser> p = new LLSDXMLParser;
+ LLPointer<LLSDXMLParser> p = new LLSDXMLParser(emit_errors);
return p->parse(str, sd, LLSDSerialize::SIZE_UNLIMITED);
}
// Line oriented parser, 30% faster than fromXML(), but can
// only be used when you know you have the complete XML
// document available in the stream.
- static S32 fromXMLDocument(LLSD& sd, std::istream& str)
+ static S32 fromXMLDocument(LLSD& sd, std::istream& str, bool emit_errors=true)
{
- LLPointer<LLSDXMLParser> p = new LLSDXMLParser();
+ LLPointer<LLSDXMLParser> p = new LLSDXMLParser(emit_errors);
return p->parseLines(str, sd);
}
- static S32 fromXML(LLSD& sd, std::istream& str)
+ static S32 fromXML(LLSD& sd, std::istream& str, bool emit_errors=true)
{
- return fromXMLEmbedded(sd, str);
-// return fromXMLDocument(sd, str);
+ return fromXMLEmbedded(sd, str, emit_errors);
+// return fromXMLDocument(sd, str, emit_errors);
}
/*
@@ -796,4 +814,8 @@ public:
}
};
+//dirty little zip functions -- yell at davep
+LL_COMMON_API std::string zip_llsd(LLSD& data);
+LL_COMMON_API bool unzip_llsd(LLSD& data, std::istream& is, S32 size);
+LL_COMMON_API U8* unzip_llsdNavMesh( bool& valid, unsigned int& outsize,std::istream& is, S32 size);
#endif // LL_LLSDSERIALIZE_H
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
index fca173df47..8d72a1c329 100644..100755
--- a/indra/llcommon/llsdserialize_xml.cpp
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -2,31 +2,25 @@
* @file llsdserialize_xml.cpp
* @brief XML parsers and formatters for LLSD
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -41,7 +35,7 @@
extern "C"
{
-#ifdef LL_STANDALONE
+#ifdef LL_USESYSTEMLIBS
# include <expat.h>
#else
# include "expat/expat.h"
@@ -174,8 +168,8 @@ S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 opti
break;
case LLSD::TypeString:
- if(data.asString().empty()) ostr << pre << "<string />" << post;
- else ostr << pre << "<string>" << escapeString(data.asString()) <<"</string>" << post;
+ if(data.asStringRef().empty()) ostr << pre << "<string />" << post;
+ else ostr << pre << "<string>" << escapeString(data.asStringRef()) <<"</string>" << post;
break;
case LLSD::TypeDate:
@@ -188,7 +182,7 @@ S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, U32 opti
case LLSD::TypeBinary:
{
- LLSD::Binary buffer = data.asBinary();
+ const LLSD::Binary& buffer = data.asBinary();
if(buffer.empty())
{
ostr << pre << "<binary />" << post;
@@ -256,7 +250,7 @@ std::string LLSDXMLFormatter::escapeString(const std::string& in)
class LLSDXMLParser::Impl
{
public:
- Impl();
+ Impl(bool emit_errors);
~Impl();
S32 parse(std::istream& input, LLSD& data);
@@ -300,6 +294,7 @@ private:
static const XML_Char* findAttribute(const XML_Char* name, const XML_Char** pairs);
+ bool mEmitErrors;
XML_Parser mParser;
@@ -321,7 +316,8 @@ private:
};
-LLSDXMLParser::Impl::Impl()
+LLSDXMLParser::Impl::Impl(bool emit_errors)
+ : mEmitErrors(emit_errors)
{
mParser = XML_ParserCreate(NULL);
reset();
@@ -405,7 +401,10 @@ S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data)
{
((char*) buffer)[count ? count - 1 : 0] = '\0';
}
- llinfos << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << llendl;
+ if (mEmitErrors)
+ {
+ LL_INFOS() << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*) buffer << LL_ENDL;
+ }
data = LLSD();
return LLSDParser::PARSE_FAILURE;
}
@@ -467,7 +466,7 @@ S32 LLSDXMLParser::Impl::parseLines(std::istream& input, LLSD& data)
}
}
- status = XML_ParseBuffer(mParser, num_read, false);
+ status = XML_ParseBuffer(mParser, (int)num_read, false);
if (status == XML_STATUS_ERROR)
{
break;
@@ -483,7 +482,10 @@ S32 LLSDXMLParser::Impl::parseLines(std::istream& input, LLSD& data)
if (status == XML_STATUS_ERROR
&& !mGracefullStop)
{
- llinfos << "LLSDXMLParser::Impl::parseLines: XML_STATUS_ERROR" << llendl;
+ if (mEmitErrors)
+ {
+ LL_INFOS() << "LLSDXMLParser::Impl::parseLines: XML_STATUS_ERROR" << LL_ENDL;
+ }
return LLSDParser::PARSE_FAILURE;
}
@@ -544,7 +546,7 @@ void LLSDXMLParser::Impl::parsePart(const char* buf, int len)
XML_Status status = XML_Parse(mParser, buf, len, false);
if (status == XML_STATUS_ERROR)
{
- llinfos << "Unexpected XML parsing error at start" << llendl;
+ LL_INFOS() << "Unexpected XML parsing error at start" << LL_ENDL;
}
}
}
@@ -722,6 +724,7 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
case ELEMENT_INTEGER:
{
S32 i;
+ // sscanf okay here with different locales - ints don't change for different locale settings like floats do.
if ( sscanf(mCurrentContent.c_str(), "%d", &i ) == 1 )
{ // See if sscanf works - it's faster
value = i;
@@ -735,15 +738,19 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
case ELEMENT_REAL:
{
- F64 r;
- if ( sscanf(mCurrentContent.c_str(), "%lf", &r ) == 1 )
- { // See if sscanf works - it's faster
- value = r;
- }
- else
- {
- value = LLSD(mCurrentContent).asReal();
- }
+ value = LLSD(mCurrentContent).asReal();
+ // removed since this breaks when locale has decimal separator that isn't '.'
+ // investigated changing local to something compatible each time but deemed higher
+ // risk that just using LLSD.asReal() each time.
+ //F64 r;
+ //if ( sscanf(mCurrentContent.c_str(), "%lf", &r ) == 1 )
+ //{ // See if sscanf works - it's faster
+ // value = r;
+ //}
+ //else
+ //{
+ // value = LLSD(mCurrentContent).asReal();
+ //}
}
break;
@@ -895,7 +902,7 @@ LLSDXMLParser::Impl::Element LLSDXMLParser::Impl::readElement(const XML_Char* na
/**
* LLSDXMLParser
*/
-LLSDXMLParser::LLSDXMLParser() : impl(* new Impl)
+LLSDXMLParser::LLSDXMLParser(bool emit_errors /* = true */) : impl(* new Impl(emit_errors))
{
}
diff --git a/indra/llcommon/llsdserialize_xml.h b/indra/llcommon/llsdserialize_xml.h
index 53aa09f8e5..dcc5f3d3c7 100644..100755
--- a/indra/llcommon/llsdserialize_xml.h
+++ b/indra/llcommon/llsdserialize_xml.h
@@ -2,31 +2,25 @@
* @file llsdserialize_xml.h
* @brief XML parsers and formatters for LLSD
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
index c8d8030e87..6ad4a97149 100644..100755
--- a/indra/llcommon/llsdutil.cpp
+++ b/indra/llcommon/llsdutil.cpp
@@ -4,31 +4,25 @@
* @date 2006-05-24
* @brief Implementation of classes, functions, etc, for using structured data.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -47,6 +41,7 @@
#include "llsdserialize.h"
#include "stringize.h"
+#include "is_approx_equal_fraction.h"
#include <map>
#include <set>
@@ -187,7 +182,7 @@ char* ll_pretty_print_sd_ptr(const LLSD* sd)
char* ll_pretty_print_sd(const LLSD& sd)
{
- const U32 bufferSize = 10 * 1024;
+ const U32 bufferSize = 100 * 1024;
static char buffer[bufferSize];
std::ostringstream stream;
//stream.rdbuf()->pubsetbuf(buffer, bufferSize);
@@ -577,7 +572,7 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str
return match_types(prototype.type(), TypeVector(), data.type(), pfx);
}
-bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
+bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits)
{
// We're comparing strict equality of LLSD representation rather than
// performing any conversions. So if the types aren't equal, the LLSD
@@ -594,6 +589,20 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
// Both are TypeUndefined. There's nothing more to know.
return true;
+ case LLSD::TypeReal:
+ // This is where the 'bits' argument comes in handy. If passed
+ // explicitly, it means to use is_approx_equal_fraction() to compare.
+ if (bits >= 0)
+ {
+ return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits);
+ }
+ // Otherwise we compare bit representations, and the usual caveats
+ // about comparing floating-point numbers apply. Omitting 'bits' when
+ // comparing Real values is only useful when we expect identical bit
+ // representation for a given Real value, e.g. for integer-valued
+ // Reals.
+ return (lhs.asReal() == rhs.asReal());
+
#define COMPARE_SCALAR(type) \
case LLSD::Type##type: \
/* LLSD::URI has operator!=() but not operator==() */ \
@@ -602,10 +611,6 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
COMPARE_SCALAR(Boolean);
COMPARE_SCALAR(Integer);
- // The usual caveats about comparing floating-point numbers apply. This is
- // only useful when we expect identical bit representation for a given
- // Real value, e.g. for integer-valued Reals.
- COMPARE_SCALAR(Real);
COMPARE_SCALAR(String);
COMPARE_SCALAR(UUID);
COMPARE_SCALAR(Date);
@@ -623,7 +628,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
for ( ; lai != laend && rai != raend; ++lai, ++rai)
{
// If any one array element is unequal, the arrays are unequal.
- if (! llsd_equals(*lai, *rai))
+ if (! llsd_equals(*lai, *rai, bits))
return false;
}
// Here we've reached the end of one or the other array. They're equal
@@ -650,7 +655,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
if (rhskeys.erase(lmi->first) != 1)
return false;
// Both maps have the current key. Compare values.
- if (! llsd_equals(lmi->second, rhs[lmi->first]))
+ if (! llsd_equals(lmi->second, rhs[lmi->first], bits))
return false;
}
// We've now established that all the lhs keys have equal values in
@@ -663,7 +668,7 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
// We expect that every possible type() value is specifically handled
// above. Failing to extend this switch to support a new LLSD type is
// an error that must be brought to the coder's attention.
- LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): "
+ LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): "
"unknown type " << lhs.type() << LL_ENDL;
return false; // pacify the compiler
}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
index 6a6c396687..99cb79aa54 100644..100755
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -4,38 +4,32 @@
* @date 2006-05-24
* @brief Utility classes, functions, etc, for using structured data.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLSDUTIL_H
#define LL_LLSDUTIL_H
-class LLSD;
+#include "llsd.h"
// U32
LL_COMMON_API LLSD ll_sd_from_U32(const U32);
@@ -129,8 +123,10 @@ LL_COMMON_API BOOL compare_llsd_with_template(
*/
LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
-/// Deep equality
-LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs);
+/// Deep equality. If you want to compare LLSD::Real values for approximate
+/// equality rather than bitwise equality, pass @a bits as for
+/// is_approx_equal_fraction().
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
// Simple function to copy data out of input & output iterators if
// there is no need for casting.
@@ -144,4 +140,283 @@ template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
return dest;
}
+/*****************************************************************************
+* LLSDArray
+*****************************************************************************/
+/**
+ * Construct an LLSD::Array inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDArray("text")(17)(3.14));
+ * @endcode
+ *
+ * For completeness, LLSDArray() with no args constructs an empty array, so
+ * <tt>LLSDArray()("text")(17)(3.14)</tt> produces an array equivalent to the
+ * above. But for most purposes, LLSD() is already equivalent to an empty
+ * array, and if you explicitly want an empty isArray(), there's
+ * LLSD::emptyArray(). However, supporting a no-args LLSDArray() constructor
+ * follows the principle of least astonishment.
+ */
+class LLSDArray
+{
+public:
+ LLSDArray():
+ _data(LLSD::emptyArray())
+ {}
+
+ /**
+ * Need an explicit copy constructor. Consider the following:
+ *
+ * @code
+ * LLSD array_of_arrays(LLSDArray(LLSDArray(17)(34))
+ * (LLSDArray("x")("y")));
+ * @endcode
+ *
+ * The coder intends to construct [[17, 34], ["x", "y"]].
+ *
+ * With the compiler's implicit copy constructor, s/he gets instead
+ * [17, 34, ["x", "y"]].
+ *
+ * The expression LLSDArray(17)(34) constructs an LLSDArray with those two
+ * values. The reader assumes it should be converted to LLSD, as we always
+ * want with LLSDArray, before passing it to the @em outer LLSDArray
+ * constructor! This copy constructor makes that happen.
+ */
+ LLSDArray(const LLSDArray& inner):
+ _data(LLSD::emptyArray())
+ {
+ _data.append(inner);
+ }
+
+ LLSDArray(const LLSD& value):
+ _data(LLSD::emptyArray())
+ {
+ _data.append(value);
+ }
+
+ LLSDArray& operator()(const LLSD& value)
+ {
+ _data.append(value);
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+/*****************************************************************************
+* LLSDMap
+*****************************************************************************/
+/**
+ * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
+ * @endcode
+ *
+ * For completeness, LLSDMap() with no args constructs an empty map, so
+ * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
+ * equivalent to the above. But for most purposes, LLSD() is already
+ * equivalent to an empty map, and if you explicitly want an empty isMap(),
+ * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
+ * constructor follows the principle of least astonishment.
+ */
+class LLSDMap
+{
+public:
+ LLSDMap():
+ _data(LLSD::emptyMap())
+ {}
+ LLSDMap(const LLSD::String& key, const LLSD& value):
+ _data(LLSD::emptyMap())
+ {
+ _data[key] = value;
+ }
+
+ LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
+ {
+ _data[key] = value;
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+/*****************************************************************************
+* LLSDParam
+*****************************************************************************/
+/**
+ * LLSDParam is a customization point for passing LLSD values to function
+ * parameters of more or less arbitrary type. LLSD provides a small set of
+ * native conversions; but if a generic algorithm explicitly constructs an
+ * LLSDParam object in the function's argument list, a consumer can provide
+ * LLSDParam specializations to support more different parameter types than
+ * LLSD's native conversions.
+ *
+ * Usage:
+ *
+ * @code
+ * void somefunc(const paramtype&);
+ * ...
+ * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
+ * @endcode
+ */
+template <typename T>
+class LLSDParam
+{
+public:
+ /**
+ * Default implementation converts to T on construction, saves converted
+ * value for later retrieval
+ */
+ LLSDParam(const LLSD& value):
+ _value(value)
+ {}
+
+ operator T() const { return _value; }
+
+private:
+ T _value;
+};
+
+/**
+ * Turns out that several target types could accept an LLSD param using any of
+ * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
+ * std::string. Therefore, the compiler can't decide which LLSD conversion
+ * operator to choose, even though to us it seems obvious. But that's okay, we
+ * can specialize LLSDParam for such target types, explicitly specifying the
+ * desired conversion -- that's part of what LLSDParam is all about. Turns out
+ * we have to do that enough to make it worthwhile generalizing. Use a macro
+ * because I need to specify one of the asReal, etc., explicit conversion
+ * methods as well as a type. If I'm overlooking a clever way to implement
+ * that using a template instead, feel free to reimplement.
+ */
+#define LLSDParam_for(T, AS) \
+template <> \
+class LLSDParam<T> \
+{ \
+public: \
+ LLSDParam(const LLSD& value): \
+ _value((T)value.AS()) \
+ {} \
+ \
+ operator T() const { return _value; } \
+ \
+private: \
+ T _value; \
+}
+
+LLSDParam_for(float, asReal);
+LLSDParam_for(LLUUID, asUUID);
+LLSDParam_for(LLDate, asDate);
+LLSDParam_for(LLURI, asURI);
+LLSDParam_for(LLSD::Binary, asBinary);
+
+/**
+ * LLSDParam<const char*> is an example of the kind of conversion you can
+ * support with LLSDParam beyond native LLSD conversions. Normally you can't
+ * pass an LLSD object to a function accepting const char* -- but you can
+ * safely pass an LLSDParam<const char*>(yourLLSD).
+ */
+template <>
+class LLSDParam<const char*>
+{
+private:
+ // The difference here is that we store a std::string rather than a const
+ // char*. It's important that the LLSDParam object own the std::string.
+ std::string _value;
+ // We don't bother storing the incoming LLSD object, but we do have to
+ // distinguish whether _value is an empty string because the LLSD object
+ // contains an empty string or because it's isUndefined().
+ bool _undefined;
+
+public:
+ LLSDParam(const LLSD& value):
+ _value(value),
+ _undefined(value.isUndefined())
+ {}
+
+ // The const char* we retrieve is for storage owned by our _value member.
+ // That's how we guarantee that the const char* is valid for the lifetime
+ // of this LLSDParam object. Constructing your LLSDParam in the argument
+ // list should ensure that the LLSDParam object will persist for the
+ // duration of the function call.
+ operator const char*() const
+ {
+ if (_undefined)
+ {
+ // By default, an isUndefined() LLSD object's asString() method
+ // will produce an empty string. But for a function accepting
+ // const char*, it's often important to be able to pass NULL, and
+ // isUndefined() seems like the best way. If you want to pass an
+ // empty string, you can still pass LLSD(""). Without this special
+ // case, though, no LLSD value could pass NULL.
+ return NULL;
+ }
+ return _value.c_str();
+ }
+};
+
+namespace llsd
+{
+
+/*****************************************************************************
+* BOOST_FOREACH() helpers for LLSD
+*****************************************************************************/
+/// Usage: BOOST_FOREACH(LLSD item, inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+ inArray(const LLSD& array):
+ _array(array)
+ {}
+
+ typedef LLSD::array_const_iterator const_iterator;
+ typedef LLSD::array_iterator iterator;
+
+ iterator begin() { return _array.beginArray(); }
+ iterator end() { return _array.endArray(); }
+ const_iterator begin() const { return _array.beginArray(); }
+ const_iterator end() const { return _array.endArray(); }
+
+private:
+ LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
+
+/// Usage: BOOST_FOREACH([const] MapEntry& e, inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+ inMap(const LLSD& map):
+ _map(map)
+ {}
+
+ typedef LLSD::map_const_iterator const_iterator;
+ typedef LLSD::map_iterator iterator;
+
+ iterator begin() { return _map.beginMap(); }
+ iterator end() { return _map.endMap(); }
+ const_iterator begin() const { return _map.beginMap(); }
+ const_iterator end() const { return _map.endMap(); }
+
+private:
+ LLSD _map;
+};
+
+} // namespace llsd
+
#endif // LL_LLSDUTIL_H
diff --git a/indra/llcommon/llsecondlifeurls.cpp b/indra/llcommon/llsecondlifeurls.cpp
deleted file mode 100644
index 9e67872ffd..0000000000
--- a/indra/llcommon/llsecondlifeurls.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/**
- * @file llsecondlifeurls.cpp
- * @brief Urls used in the product
- *
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-#include "llsecondlifeurls.h"
-/*
-const std::string CREATE_ACCOUNT_URL (
- "http://join.secondlife.com/");
-
-const std::string MANAGE_ACCOUNT (
- "http://secondlife.com/account/"); // *TODO: NOT USED
-
-const std::string AUCTION_URL (
- "http://secondlife.com/auctions/auction-detail.php?id=");
-
-const std::string EVENTS_URL (
- "http://secondlife.com/events/");
-*/
-const std::string TIER_UP_URL (
- "http://secondlife.com/app/landtier"); // *TODO: Translate (simulator)
-
-const std::string DIRECTX_9_URL (
- "http://secondlife.com/support/"); // *TODO: NOT USED
-/*
-const std::string LAND_URL (
- "http://secondlife.com/app/landtier"); // *TODO: NOT USED
-
-const std::string UPGRADE_TO_PREMIUM_URL (
- "http://secondlife.com/app/upgrade/"); // *TODO: NOT USED
-
-const std::string AMD_AGP_URL (
- "http://secondlife.com/support/"); // *TODO: NOT USED
-
-const std::string VIA_URL (
- "http://secondlife.com/support/"); // *TODO: NOT USED
-
-const std::string SUPPORT_URL (
- "http://secondlife.com/support/");
-
-const std::string INTEL_CHIPSET_URL (
- "http://secondlife.com/support/"); // *TODO: NOT USED
-
-const std::string SIS_CHIPSET_URL (
- "http://secondlife.com/support/"); // *TODO: NOT USED
-
-const std::string BLOGS_URL (
- "http://blog.secondlife.com/"); // *TODO: NOT USED
-
-const std::string BUY_CURRENCY_URL (
- "http://secondlife.com/app/currency/");
-
-const std::string LSL_DOC_URL (
- "http://secondlife.com/app/lsldoc/"); // *TODO: NOT USED
-
-const std::string SL_KB_URL (
- "http://secondlife.com/knowledgebase/"); // *TODO: NOT USED
-
-const std::string RELEASE_NOTES_BASE_URL (
- "http://secondlife.com/app/releasenotes/");
-*/
-
diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h
deleted file mode 100644
index bd2f9f7604..0000000000
--- a/indra/llcommon/llsecondlifeurls.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * @file llsecondlifeurls.h
- * @brief Global URLs to pages on our web site
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSECONDLIFEURLS_H
-#define LL_LLSECONDLIFEURLS_H
-/*
-// Account registration web page
-LL_COMMON_API extern const std::string CREATE_ACCOUNT_URL;
-
-// Manage Account
-LL_COMMON_API extern const std::string MANAGE_ACCOUNT;
-
-LL_COMMON_API extern const std::string AUCTION_URL;
-
-LL_COMMON_API extern const std::string EVENTS_URL;
-*/
-// Tier up to a new land level.
-LL_COMMON_API extern const std::string TIER_UP_URL;
-
-
-// Tier up to a new land level.
-LL_COMMON_API extern const std::string LAND_URL;
-
-// How to get DirectX 9
-LL_COMMON_API extern const std::string DIRECTX_9_URL;
-
-/*
-// Upgrade from basic membership to premium membership
-LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL;
-
-
-// Out of date VIA chipset
-LL_COMMON_API extern const std::string VIA_URL;
-
-// Support URL
-LL_COMMON_API extern const std::string SUPPORT_URL;
-
-// Linden Blogs page
-LL_COMMON_API extern const std::string BLOGS_URL;
-
-// Currency page
-LL_COMMON_API extern const std::string BUY_CURRENCY_URL;
-
-// LSL script wiki
-LL_COMMON_API extern const std::string LSL_DOC_URL;
-
-// SL KnowledgeBase page
-LL_COMMON_API extern const std::string SL_KB_URL;
-
-// Release Notes Redirect URL for Server and Viewer
-LL_COMMON_API extern const std::string RELEASE_NOTES_BASE_URL;
-*/
-#endif
diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h
index 5df93b646e..727b4568d8 100644..100755
--- a/indra/llcommon/llsimplehash.h
+++ b/indra/llcommon/llsimplehash.h
@@ -1,31 +1,25 @@
/**
* @file llsimplehash.h
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index 6b5feaf1c4..9b49e52377 100644..100755
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -2,31 +2,25 @@
* @file llsingleton.cpp
* @author Brad Kittenbrink
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,5 +28,4 @@
#include "llsingleton.h"
-std::map<std::string, void *> * LLSingletonRegistry::sSingletonMap = NULL;
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index ddeb4d1489..6e6291a165 100644..100755
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -1,31 +1,25 @@
/**
* @file llsingleton.h
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LLSINGLETON_H
@@ -36,38 +30,6 @@
#include <typeinfo>
#include <boost/noncopyable.hpp>
-/// @brief A global registry of all singletons to prevent duplicate allocations
-/// across shared library boundaries
-class LL_COMMON_API LLSingletonRegistry {
- private:
- typedef std::map<std::string, void *> TypeMap;
- static TypeMap * sSingletonMap;
-
- static void checkInit()
- {
- if(sSingletonMap == NULL)
- {
- sSingletonMap = new TypeMap();
- }
- }
-
- public:
- template<typename T> static void * & get()
- {
- std::string name(typeid(T).name());
-
- checkInit();
-
- // the first entry of the pair returned by insert will be either the existing
- // iterator matching our key, or the newly inserted NULL initialized entry
- // see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html
- TypeMap::iterator result =
- sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first;
-
- return result->second;
- }
-};
-
// 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.
@@ -105,78 +67,108 @@ private:
INITIALIZED,
DELETED
} EInitState;
-
- static void deleteSingleton()
- {
- delete getData().mSingletonInstance;
- getData().mSingletonInstance = NULL;
- }
+
+ static DERIVED_TYPE* constructSingleton()
+ {
+ return new DERIVED_TYPE();
+ }
// stores pointer to singleton instance
- // and tracks initialization state of singleton
- struct SingletonInstanceData
+ struct SingletonLifetimeManager
{
- EInitState mInitState;
- DERIVED_TYPE* mSingletonInstance;
-
- SingletonInstanceData()
- : mSingletonInstance(NULL),
- mInitState(UNINITIALIZED)
- {}
-
- ~SingletonInstanceData()
+ SingletonLifetimeManager()
+ {
+ construct();
+ }
+
+ static void construct()
{
- deleteSingleton();
+ sData.mInitState = CONSTRUCTING;
+ sData.mInstance = constructSingleton();
+ sData.mInitState = INITIALIZING;
+ }
+
+ ~SingletonLifetimeManager()
+ {
+ if (sData.mInitState != DELETED)
+ {
+ deleteSingleton();
+ }
}
};
public:
virtual ~LLSingleton()
{
- SingletonInstanceData& data = getData();
- data.mSingletonInstance = NULL;
- data.mInitState = DELETED;
+ sData.mInstance = NULL;
+ sData.mInitState = DELETED;
}
- static SingletonInstanceData& getData()
+ /**
+ * @brief Immediately delete the singleton.
+ *
+ * A subsequent call to LLProxy::getInstance() will construct a new
+ * instance of the class.
+ *
+ * LLSingletons are normally destroyed after main() has exited and the C++
+ * runtime is cleaning up statically-constructed objects. Some classes
+ * derived from LLSingleton have objects that are part of a runtime system
+ * that is terminated before main() exits. Calling the destructor of those
+ * objects after the termination of their respective systems can cause
+ * crashes and other problems during termination of the project. Using this
+ * method to destroy the singleton early can prevent these crashes.
+ *
+ * An example where this is needed is for a LLSingleton that has an APR
+ * object as a member that makes APR calls on destruction. The APR system is
+ * shut down explicitly before main() exits. This causes a crash on exit.
+ * Using this method before the call to apr_terminate() and NOT calling
+ * getInstance() again will prevent the crash.
+ */
+ static void deleteSingleton()
{
- // this is static to cache the lookup results
- static void * & registry = LLSingletonRegistry::get<DERIVED_TYPE>();
-
- // *TODO - look into making this threadsafe
- if(NULL == registry)
- {
- static SingletonInstanceData data;
- registry = &data;
- }
-
- return *static_cast<SingletonInstanceData *>(registry);
+ delete sData.mInstance;
+ sData.mInstance = NULL;
+ sData.mInitState = DELETED;
}
+
static DERIVED_TYPE* getInstance()
{
- SingletonInstanceData& data = getData();
+ static SingletonLifetimeManager sLifeTimeMgr;
- if (data.mInitState == CONSTRUCTING)
+ switch (sData.mInitState)
{
- llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
+ case UNINITIALIZED:
+ // should never be uninitialized at this point
+ llassert(false);
+ return NULL;
+ case CONSTRUCTING:
+ LL_ERRS() << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << LL_ENDL;
+ return NULL;
+ case INITIALIZING:
+ // go ahead and flag ourselves as initialized so we can be reentrant during initialization
+ sData.mInitState = INITIALIZED;
+ // initialize singleton after constructing it so that it can reference other singletons which in turn depend on it,
+ // thus breaking cyclic dependencies
+ sData.mInstance->initSingleton();
+ return sData.mInstance;
+ case INITIALIZED:
+ return sData.mInstance;
+ case DELETED:
+ LL_WARNS() << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << LL_ENDL;
+ SingletonLifetimeManager::construct();
+ // same as first time construction
+ sData.mInitState = INITIALIZED;
+ sData.mInstance->initSingleton();
+ return sData.mInstance;
}
- if (data.mInitState == DELETED)
- {
- llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
- }
-
- if (!data.mSingletonInstance)
- {
- data.mInitState = CONSTRUCTING;
- data.mSingletonInstance = new DERIVED_TYPE();
- data.mInitState = INITIALIZING;
- data.mSingletonInstance->initSingleton();
- data.mInitState = INITIALIZED;
- }
-
- return data.mSingletonInstance;
+ return NULL;
+ }
+
+ static DERIVED_TYPE* getIfExists()
+ {
+ return sData.mInstance;
}
// Reference version of getInstance()
@@ -190,18 +182,31 @@ public:
// Use this to avoid accessing singletons before the can safely be constructed
static bool instanceExists()
{
- return getData().mInitState == INITIALIZED;
+ return sData.mInitState == INITIALIZED;
}
// Has this singleton already been deleted?
// Use this to avoid accessing singletons from a static object's destructor
static bool destroyed()
{
- return getData().mInitState == DELETED;
+ return sData.mInitState == DELETED;
}
private:
+
virtual void initSingleton() {}
+
+ struct SingletonData
+ {
+ // explicitly has a default constructor so that member variables are zero initialized in BSS
+ // and only changed by singleton logic, not constructor running during startup
+ EInitState mInitState;
+ DERIVED_TYPE* mInstance;
+ };
+ static SingletonData sData;
};
+template<typename T>
+typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;
+
#endif
diff --git a/indra/llcommon/llskiplist.h b/indra/llcommon/llskiplist.h
deleted file mode 100644
index 33a8687ad5..0000000000
--- a/indra/llcommon/llskiplist.h
+++ /dev/null
@@ -1,523 +0,0 @@
-/**
- * @file llskiplist.h
- * @brief skip list implementation
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LL_LLSKIPLIST_H
-#define LL_LLSKIPLIST_H
-
-#include "llrand.h"
-#include "llrand.h"
-
-// NOTA BENE: Insert first needs to be < NOT <=
-// Binary depth must be >= 2
-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
-//
-
-
-// Binary depth must be >= 2
-template <class DATA_TYPE, S32 BINARY_DEPTH>
-inline void LLSkipList<DATA_TYPE, BINARY_DEPTH>::init()
-{
- 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 (ll_frand() < 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
deleted file mode 100644
index 6bbca8f67c..0000000000
--- a/indra/llcommon/llskipmap.h
+++ /dev/null
@@ -1,1028 +0,0 @@
-/**
- * @file llskipmap.h
- * @brief Associative container based on the skiplist algorithm.
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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/llsmoothstep.h b/indra/llcommon/llsmoothstep.h
index 153c83d3d5..1f97a3ec89 100644..100755
--- a/indra/llcommon/llsmoothstep.h
+++ b/indra/llcommon/llsmoothstep.h
@@ -2,31 +2,25 @@
* @file llsmoothstep.h
* @brief Smoothstep - transition from 0 to 1 - function, first and second derivatives all continuous (smooth)
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llstack.h b/indra/llcommon/llstack.h
deleted file mode 100644
index 66175c7482..0000000000
--- a/indra/llcommon/llstack.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * @file llstack.h
- * @brief LLStack template class
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp
index 6558df70a4..bbf0e1e141 100644..100755
--- a/indra/llcommon/llstacktrace.cpp
+++ b/indra/llcommon/llstacktrace.cpp
@@ -2,31 +2,25 @@
* @file llstacktrace.cpp
* @brief stack tracing functionality
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -38,7 +32,7 @@
#include <iostream>
#include <sstream>
-#include "windows.h"
+#include "llwin32headerslean.h"
#include "Dbghelp.h"
typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
@@ -131,6 +125,30 @@ bool ll_get_stack_trace(std::vector<std::string>& lines)
return false;
}
+void ll_get_stack_trace_internal(std::vector<std::string>& lines)
+{
+ const S32 MAX_STACK_DEPTH = 100;
+ const S32 STRING_NAME_LENGTH = 256;
+
+ HANDLE process = GetCurrentProcess();
+ SymInitialize( process, NULL, TRUE );
+
+ void *stack[MAX_STACK_DEPTH];
+
+ unsigned short frames = RtlCaptureStackBackTrace_fn( 0, MAX_STACK_DEPTH, stack, NULL );
+ SYMBOL_INFO *symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + STRING_NAME_LENGTH * sizeof(char), 1);
+ symbol->MaxNameLen = STRING_NAME_LENGTH-1;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+
+ for(unsigned int i = 0; i < frames; i++)
+ {
+ SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
+ lines.push_back(symbol->Name);
+ }
+
+ free( symbol );
+}
+
#else
bool ll_get_stack_trace(std::vector<std::string>& lines)
@@ -138,5 +156,10 @@ bool ll_get_stack_trace(std::vector<std::string>& lines)
return false;
}
+void ll_get_stack_trace_internal(std::vector<std::string>& lines)
+{
+
+}
+
#endif
diff --git a/indra/llcommon/llstacktrace.h b/indra/llcommon/llstacktrace.h
index 9f857f0fd3..335765386a 100644..100755
--- a/indra/llcommon/llstacktrace.h
+++ b/indra/llcommon/llstacktrace.h
@@ -2,31 +2,25 @@
* @file llstacktrace.h
* @brief stack trace functions
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,6 +33,7 @@
#include <string>
LL_COMMON_API bool ll_get_stack_trace(std::vector<std::string>& lines);
+LL_COMMON_API void ll_get_stack_trace_internal(std::vector<std::string>& lines);
#endif
diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp
deleted file mode 100644
index 0bd2609f4a..0000000000
--- a/indra/llcommon/llstat.cpp
+++ /dev/null
@@ -1,1312 +0,0 @@
-/**
- * @file llstat.cpp
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "llstat.h"
-#include "lllivefile.h"
-#include "llerrorcontrol.h"
-#include "llframetimer.h"
-#include "timing.h"
-#include "llsd.h"
-#include "llsdserialize.h"
-#include "llstl.h"
-#include "u64.h"
-
-
-// statics
-S32 LLPerfBlock::sStatsFlags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS; // Control what is being recorded
-LLPerfBlock::stat_map_t LLPerfBlock::sStatMap; // Map full path string to LLStatTime objects, tracks all active objects
-std::string LLPerfBlock::sCurrentStatPath = ""; // Something like "/total_time/physics/physics step"
-LLStat::stat_map_t LLStat::sStatList;
-
-//------------------------------------------------------------------------
-// Live config file to trigger stats logging
-static const char STATS_CONFIG_FILE_NAME[] = "/dev/shm/simperf/simperf_proc_config.llsd";
-static const F32 STATS_CONFIG_REFRESH_RATE = 5.0; // seconds
-
-class LLStatsConfigFile : public LLLiveFile
-{
-public:
- LLStatsConfigFile()
- : LLLiveFile(filename(), STATS_CONFIG_REFRESH_RATE),
- mChanged(false), mStatsp(NULL) { }
-
- static std::string filename();
-
-protected:
- /* virtual */ bool loadFile();
-
-public:
- void init(LLPerfStats* statsp);
- static LLStatsConfigFile& instance();
- // return the singleton stats config file
-
- bool mChanged;
-
-protected:
- LLPerfStats* mStatsp;
-};
-
-std::string LLStatsConfigFile::filename()
-{
- return STATS_CONFIG_FILE_NAME;
-}
-
-void LLStatsConfigFile::init(LLPerfStats* statsp)
-{
- mStatsp = statsp;
-}
-
-LLStatsConfigFile& LLStatsConfigFile::instance()
-{
- static LLStatsConfigFile the_file;
- return the_file;
-}
-
-
-/* virtual */
-// Load and parse the stats configuration file
-bool LLStatsConfigFile::loadFile()
-{
- if (!mStatsp)
- {
- llwarns << "Tries to load performance configure file without initializing LPerfStats" << llendl;
- return false;
- }
- mChanged = true;
-
- LLSD stats_config;
- {
- llifstream file(filename().c_str());
- if (file.is_open())
- {
- LLSDSerialize::fromXML(stats_config, file);
- if (stats_config.isUndefined())
- {
- llinfos << "Performance statistics configuration file ill-formed, not recording statistics" << llendl;
- mStatsp->setReportPerformanceDuration( 0.f );
- return false;
- }
- }
- else
- { // File went away, turn off stats if it was on
- if ( mStatsp->frameStatsIsRunning() )
- {
- llinfos << "Performance statistics configuration file deleted, not recording statistics" << llendl;
- mStatsp->setReportPerformanceDuration( 0.f );
- }
- return true;
- }
- }
-
- F32 duration = 0.f;
- F32 interval = 0.f;
- S32 flags = LLPerfBlock::LLSTATS_BASIC_STATS;
-
- const char * w = "duration";
- if (stats_config.has(w))
- {
- duration = (F32)stats_config[w].asReal();
- }
- w = "interval";
- if (stats_config.has(w))
- {
- interval = (F32)stats_config[w].asReal();
- }
- w = "flags";
- if (stats_config.has(w))
- {
- flags = (S32)stats_config[w].asInteger();
- if (flags == LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS &&
- duration > 0)
- { // No flags passed in, but have a duration, so reset to basic stats
- flags = LLPerfBlock::LLSTATS_BASIC_STATS;
- }
- }
-
- mStatsp->setReportPerformanceDuration( duration, flags );
- mStatsp->setReportPerformanceInterval( interval );
-
- if ( duration > 0 )
- {
- if ( interval == 0.f )
- {
- llinfos << "Recording performance stats every frame for " << duration << " sec" << llendl;
- }
- else
- {
- llinfos << "Recording performance stats every " << interval << " seconds for " << duration << " seconds" << llendl;
- }
- }
- else
- {
- llinfos << "Performance stats recording turned off" << llendl;
- }
- return true;
-}
-
-
-//------------------------------------------------------------------------
-
-LLPerfStats::LLPerfStats(const std::string& process_name, S32 process_pid) :
- mFrameStatsFileFailure(FALSE),
- mSkipFirstFrameStats(FALSE),
- mProcessName(process_name),
- mProcessPID(process_pid),
- mReportPerformanceStatInterval(1.f),
- mReportPerformanceStatEnd(0.0)
-{ }
-
-LLPerfStats::~LLPerfStats()
-{
- LLPerfBlock::clearDynamicStats();
- mFrameStatsFile.close();
-}
-
-void LLPerfStats::init()
-{
- // Initialize the stats config file instance.
- (void) LLStatsConfigFile::instance().init(this);
- (void) LLStatsConfigFile::instance().checkAndReload();
-}
-
-// Open file for statistics
-void LLPerfStats::openPerfStatsFile()
-{
- if ( !mFrameStatsFile
- && !mFrameStatsFileFailure )
- {
- std::string stats_file = llformat("/dev/shm/simperf/%s_proc.%d.llsd", mProcessName.c_str(), mProcessPID);
- mFrameStatsFile.close();
- mFrameStatsFile.clear();
- mFrameStatsFile.open(stats_file, llofstream::out);
- if ( mFrameStatsFile.fail() )
- {
- llinfos << "Error opening statistics log file " << stats_file << llendl;
- mFrameStatsFileFailure = TRUE;
- }
- else
- {
- LLSD process_info = LLSD::emptyMap();
- process_info["name"] = mProcessName;
- process_info["pid"] = (LLSD::Integer) mProcessPID;
- process_info["stat_rate"] = (LLSD::Integer) mReportPerformanceStatInterval;
- // Add process-specific info.
- addProcessHeaderInfo(process_info);
-
- mFrameStatsFile << LLSDNotationStreamer(process_info) << std::endl;
- }
- }
-}
-
-// Dump out performance metrics over some time interval
-void LLPerfStats::dumpIntervalPerformanceStats()
-{
- // Ensure output file is OK
- openPerfStatsFile();
-
- if ( mFrameStatsFile )
- {
- LLSD stats = LLSD::emptyMap();
-
- LLStatAccum::TimeScale scale;
- if ( getReportPerformanceInterval() == 0.f )
- {
- scale = LLStatAccum::SCALE_PER_FRAME;
- }
- else if ( getReportPerformanceInterval() < 0.5f )
- {
- scale = LLStatAccum::SCALE_100MS;
- }
- else
- {
- scale = LLStatAccum::SCALE_SECOND;
- }
-
- // Write LLSD into log
- stats["utc_time"] = (LLSD::String) LLError::utcTime();
- stats["timestamp"] = U64_to_str((totalTime() / 1000) + (gUTCOffset * 1000)); // milliseconds since epoch
- stats["frame_number"] = (LLSD::Integer) LLFrameTimer::getFrameCount();
-
- // Add process-specific frame info.
- addProcessFrameInfo(stats, scale);
- LLPerfBlock::addStatsToLLSDandReset( stats, scale );
-
- mFrameStatsFile << LLSDNotationStreamer(stats) << std::endl;
- }
-}
-
-// Set length of performance stat recording.
-// If turning stats on, caller must provide flags
-void LLPerfStats::setReportPerformanceDuration( F32 seconds, S32 flags /* = LLSTATS_NO_OPTIONAL_STATS */ )
-{
- if ( seconds <= 0.f )
- {
- mReportPerformanceStatEnd = 0.0;
- LLPerfBlock::setStatsFlags(LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS); // Make sure all recording is off
- mFrameStatsFile.close();
- LLPerfBlock::clearDynamicStats();
- }
- else
- {
- mReportPerformanceStatEnd = LLFrameTimer::getElapsedSeconds() + ((F64) seconds);
- // Clear failure flag to try and create the log file once
- mFrameStatsFileFailure = FALSE;
- mSkipFirstFrameStats = TRUE; // Skip the first report (at the end of this frame)
- LLPerfBlock::setStatsFlags(flags);
- }
-}
-
-void LLPerfStats::updatePerFrameStats()
-{
- (void) LLStatsConfigFile::instance().checkAndReload();
- static LLFrameTimer performance_stats_timer;
- if ( frameStatsIsRunning() )
- {
- if ( mReportPerformanceStatInterval == 0 )
- { // Record info every frame
- if ( mSkipFirstFrameStats )
- { // Skip the first time - was started this frame
- mSkipFirstFrameStats = FALSE;
- }
- else
- {
- dumpIntervalPerformanceStats();
- }
- }
- else
- {
- performance_stats_timer.setTimerExpirySec( getReportPerformanceInterval() );
- if (performance_stats_timer.checkExpirationAndReset( mReportPerformanceStatInterval ))
- {
- dumpIntervalPerformanceStats();
- }
- }
-
- if ( LLFrameTimer::getElapsedSeconds() > mReportPerformanceStatEnd )
- { // Reached end of time, clear it to stop reporting
- setReportPerformanceDuration(0.f); // Don't set mReportPerformanceStatEnd directly
- llinfos << "Recording performance stats completed" << llendl;
- }
- }
-}
-
-
-//------------------------------------------------------------------------
-
-U64 LLStatAccum::sScaleTimes[NUM_SCALES] =
-{
- USEC_PER_SEC / 10, // 100 millisec
- USEC_PER_SEC * 1, // seconds
- USEC_PER_SEC * 60, // minutes
-#if ENABLE_LONG_TIME_STATS
- // 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::LLStatAccum(bool useFrameTimer)
- : mUseFrameTimer(useFrameTimer),
- mRunning(FALSE),
- mLastTime(0),
- mLastSampleValue(0.0),
- mLastSampleValid(FALSE)
-{
-}
-
-LLStatAccum::~LLStatAccum()
-{
-}
-
-
-
-void LLStatAccum::reset(U64 when)
-{
- mRunning = TRUE;
- mLastTime = when;
-
- for (int i = 0; i < NUM_SCALES; ++i)
- {
- mBuckets[i].accum = 0.0;
- mBuckets[i].endTime = when + sScaleTimes[i];
- mBuckets[i].lastValid = false;
- }
-}
-
-void LLStatAccum::sum(F64 value)
-{
- sum(value, getCurrentUsecs());
-}
-
-void LLStatAccum::sum(F64 value, U64 when)
-{
- if (!mRunning)
- {
- reset(when);
- return;
- }
- if (when < mLastTime)
- {
- // This happens a LOT on some dual core systems.
- lldebugs << "LLStatAccum::sum clock has gone backwards from "
- << mLastTime << " to " << when << ", resetting" << llendl;
-
- reset(when);
- return;
- }
-
- // how long is this value for
- U64 timeSpan = when - mLastTime;
-
- for (int i = 0; i < NUM_SCALES; ++i)
- {
- Bucket& bucket = mBuckets[i];
-
- if (when < bucket.endTime)
- {
- bucket.accum += value;
- }
- else
- {
- U64 timeScale = sScaleTimes[i];
-
- 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::meanValue(TimeScale scale) const
-{
- if (!mRunning)
- {
- return 0.0;
- }
- if ( scale == SCALE_PER_FRAME )
- { // Per-frame not supported here
- scale = SCALE_100MS;
- }
-
- if (scale < 0 || scale >= 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::getCurrentUsecs() const
-{
- if (mUseFrameTimer)
- {
- return LLFrameTimer::getTotalTime();
- }
- else
- {
- return totalTime();
- }
-}
-
-
-// ------------------------------------------------------------------------
-
-LLStatRate::LLStatRate(bool use_frame_timer)
- : LLStatAccum(use_frame_timer)
-{
-}
-
-void LLStatRate::count(U32 value)
-{
- sum((F64)value * sScaleTimes[SCALE_SECOND]);
-}
-
-
-void LLStatRate::mark()
- {
- // Effectively the same as count(1), but sets mLastSampleValue
- U64 when = getCurrentUsecs();
-
- if ( mRunning
- && (when > mLastTime) )
- { // Set mLastSampleValue to the time from the last mark()
- F64 duration = ((F64)(when - mLastTime)) / sScaleTimes[SCALE_SECOND];
- if ( duration > 0.0 )
- {
- mLastSampleValue = 1.0 / duration;
- }
- else
- {
- mLastSampleValue = 0.0;
- }
- }
-
- sum( (F64) sScaleTimes[SCALE_SECOND], when);
- }
-
-
-// ------------------------------------------------------------------------
-
-
-LLStatMeasure::LLStatMeasure(bool use_frame_timer)
- : LLStatAccum(use_frame_timer)
-{
-}
-
-void LLStatMeasure::sample(F64 value)
-{
- U64 when = getCurrentUsecs();
-
- if (mLastSampleValid)
- {
- F64 avgValue = (value + mLastSampleValue) / 2.0;
- F64 interval = (F64)(when - mLastTime);
-
- sum(avgValue * interval, when);
- }
- else
- {
- reset(when);
- }
-
- mLastSampleValid = TRUE;
- mLastSampleValue = value;
-}
-
-
-// ------------------------------------------------------------------------
-
-LLStatTime::LLStatTime(const std::string & key)
- : LLStatAccum(false),
- mFrameNumber(LLFrameTimer::getFrameCount()),
- mTotalTimeInFrame(0),
- mKey(key)
-#if LL_DEBUG
- , mRunning(FALSE)
-#endif
-{
-}
-
-void LLStatTime::start()
-{
- // Reset frame accumluation if the frame number has changed
- U32 frame_number = LLFrameTimer::getFrameCount();
- if ( frame_number != mFrameNumber )
- {
- mFrameNumber = frame_number;
- mTotalTimeInFrame = 0;
- }
-
- sum(0.0);
-
-#if LL_DEBUG
- // Shouldn't be running already
- llassert( !mRunning );
- mRunning = TRUE;
-#endif
-}
-
-void LLStatTime::stop()
-{
- U64 end_time = getCurrentUsecs();
- U64 duration = end_time - mLastTime;
- sum(F64(duration), end_time);
- //llinfos << "mTotalTimeInFrame incremented from " << mTotalTimeInFrame << " to " << (mTotalTimeInFrame + duration) << llendl;
- mTotalTimeInFrame += duration;
-
-#if LL_DEBUG
- mRunning = FALSE;
-#endif
-}
-
-/* virtual */ F32 LLStatTime::meanValue(TimeScale scale) const
-{
- if ( LLStatAccum::SCALE_PER_FRAME == scale )
- {
- return mTotalTimeInFrame;
- }
- else
- {
- return LLStatAccum::meanValue(scale);
- }
-}
-
-
-// ------------------------------------------------------------------------
-
-
-// Use this constructor for pre-defined LLStatTime objects
-LLPerfBlock::LLPerfBlock(LLStatTime* stat ) : mPredefinedStat(stat), mDynamicStat(NULL)
-{
- if (mPredefinedStat)
- {
- // If dynamic stats are turned on, this will create a separate entry in the stat map.
- initDynamicStat(mPredefinedStat->mKey);
-
- // Start predefined stats. These stats are not part of the stat map.
- mPredefinedStat->start();
- }
-}
-
-// Use this constructor for normal, optional LLPerfBlock time slices
-LLPerfBlock::LLPerfBlock( const char* key ) : mPredefinedStat(NULL), mDynamicStat(NULL)
-{
- if ((sStatsFlags & LLSTATS_BASIC_STATS) == 0)
- { // These are off unless the base set is enabled
- return;
- }
-
- initDynamicStat(key);
-}
-
-
-// Use this constructor for dynamically created LLPerfBlock time slices
-// that are only enabled by specific control flags
-LLPerfBlock::LLPerfBlock( const char* key1, const char* key2, S32 flags ) : mPredefinedStat(NULL), mDynamicStat(NULL)
-{
- if ((sStatsFlags & flags) == 0)
- {
- return;
- }
-
- if (NULL == key2 || strlen(key2) == 0)
- {
- initDynamicStat(key1);
- }
- else
- {
- std::ostringstream key;
- key << key1 << "_" << key2;
- initDynamicStat(key.str());
- }
-}
-
-// Set up the result data map if dynamic stats are enabled
-void LLPerfBlock::initDynamicStat(const std::string& key)
-{
- // Early exit if dynamic stats aren't enabled.
- if (sStatsFlags == LLSTATS_NO_OPTIONAL_STATS)
- return;
-
- mLastPath = sCurrentStatPath; // Save and restore current path
- sCurrentStatPath += "/" + key; // Add key to current path
-
- // See if the LLStatTime object already exists
- stat_map_t::iterator iter = sStatMap.find(sCurrentStatPath);
- if ( iter == sStatMap.end() )
- {
- // StatEntry object doesn't exist, so create it
- mDynamicStat = new StatEntry( key );
- sStatMap[ sCurrentStatPath ] = mDynamicStat; // Set the entry for this path
- }
- else
- {
- // Found this path in the map, use the object there
- mDynamicStat = (*iter).second; // Get StatEntry for the current path
- }
-
- if (mDynamicStat)
- {
- mDynamicStat->mStat.start();
- mDynamicStat->mCount++;
- }
- else
- {
- llwarns << "Initialized NULL dynamic stat at '" << sCurrentStatPath << "'" << llendl;
- sCurrentStatPath = mLastPath;
- }
-}
-
-
-// Destructor does the time accounting
-LLPerfBlock::~LLPerfBlock()
-{
- if (mPredefinedStat) mPredefinedStat->stop();
- if (mDynamicStat)
- {
- mDynamicStat->mStat.stop();
- sCurrentStatPath = mLastPath; // Restore the path in case sStatsEnabled changed during this block
- }
-}
-
-
-// Clear the map of any dynamic stats. Static routine
-void LLPerfBlock::clearDynamicStats()
-{
- std::for_each(sStatMap.begin(), sStatMap.end(), DeletePairedPointer());
- sStatMap.clear();
-}
-
-// static - Extract the stat info into LLSD
-void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats,
- LLStatAccum::TimeScale scale )
-{
- // If we aren't in per-frame scale, we need to go from second to microsecond.
- U32 scale_adjustment = 1;
- if (LLStatAccum::SCALE_PER_FRAME != scale)
- {
- scale_adjustment = USEC_PER_SEC;
- }
- stat_map_t::iterator iter = sStatMap.begin();
- for ( ; iter != sStatMap.end(); ++iter )
- { // Put the entry into LLSD "/full/path/to/stat/" = microsecond total time
- const std::string & stats_full_path = (*iter).first;
-
- StatEntry * stat = (*iter).second;
- if (stat)
- {
- if (stat->mCount > 0)
- {
- stats[stats_full_path] = LLSD::emptyMap();
- stats[stats_full_path]["us"] = (LLSD::Integer) (scale_adjustment * stat->mStat.meanValue(scale));
- if (stat->mCount > 1)
- {
- stats[stats_full_path]["count"] = (LLSD::Integer) stat->mCount;
- }
- stat->mCount = 0;
- }
- }
- else
- { // WTF? Shouldn't have a NULL pointer in the map.
- llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl;
- }
- }
-}
-
-
-// ------------------------------------------------------------------------
-
-LLTimer LLStat::sTimer;
-LLFrameTimer LLStat::sFrameTimer;
-
-void LLStat::init()
-{
- llassert(mNumBins > 0);
- mNumValues = 0;
- mLastValue = 0.f;
- mLastTime = 0.f;
- mCurBin = (mNumBins-1);
- mNextBin = 0;
- mBins = new F32[mNumBins];
- mBeginTime = new F64[mNumBins];
- mTime = new F64[mNumBins];
- mDT = new F32[mNumBins];
- for (U32 i = 0; i < mNumBins; i++)
- {
- mBins[i] = 0.f;
- mBeginTime[i] = 0.0;
- mTime[i] = 0.0;
- mDT[i] = 0.f;
- }
-
- if (!mName.empty())
- {
- stat_map_t::iterator iter = sStatList.find(mName);
- if (iter != sStatList.end())
- llwarns << "LLStat with duplicate name: " << mName << llendl;
- sStatList.insert(std::make_pair(mName, this));
- }
-}
-
-LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
- : mUseFrameTimer(use_frame_timer),
- mNumBins(num_bins)
-{
- init();
-}
-
-LLStat::LLStat(std::string name, U32 num_bins, BOOL use_frame_timer)
- : mUseFrameTimer(use_frame_timer),
- mNumBins(num_bins),
- mName(name)
-{
- init();
-}
-
-LLStat::~LLStat()
-{
- delete[] mBins;
- delete[] mBeginTime;
- delete[] mTime;
- delete[] mDT;
-
- if (!mName.empty())
- {
- // handle multiple entries with the same name
- stat_map_t::iterator iter = sStatList.find(mName);
- while (iter != sStatList.end() && iter->second != this)
- ++iter;
- sStatList.erase(iter);
- }
-}
-
-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
deleted file mode 100644
index bd73c9a6bb..0000000000
--- a/indra/llcommon/llstat.h
+++ /dev/null
@@ -1,359 +0,0 @@
-/**
- * @file llstat.h
- * @brief Runtime statistics accumulation.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSTAT_H
-#define LL_LLSTAT_H
-
-#include <deque>
-#include <map>
-
-#include "lltimer.h"
-#include "llframetimer.h"
-#include "llfile.h"
-
-class LLSD;
-
-// Set this if longer stats are needed
-#define ENABLE_LONG_TIME_STATS 0
-
-//
-// 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 LL_COMMON_API LLStatAccum
-{
-protected:
- LLStatAccum(bool use_frame_timer);
- virtual ~LLStatAccum();
-
-public:
- enum TimeScale {
- SCALE_100MS,
- SCALE_SECOND,
- SCALE_MINUTE,
-#if ENABLE_LONG_TIME_STATS
- SCALE_HOUR,
- SCALE_DAY,
- SCALE_WEEK,
-#endif
- NUM_SCALES, // Use to size storage arrays
- SCALE_PER_FRAME // For latest frame information - should be after NUM_SCALES since this doesn't go into the time buckets
- };
-
- static U64 sScaleTimes[NUM_SCALES];
-
- virtual F32 meanValue(TimeScale scale) const;
- // see the subclasses for the specific meaning of value
-
- F32 meanValueOverLast100ms() const { return meanValue(SCALE_100MS); }
- F32 meanValueOverLastSecond() const { return meanValue(SCALE_SECOND); }
- F32 meanValueOverLastMinute() const { return meanValue(SCALE_MINUTE); }
-
- void reset(U64 when);
-
- void sum(F64 value);
- void sum(F64 value, U64 when);
-
- U64 getCurrentUsecs() const;
- // Get current microseconds based on timer type
-
- BOOL mUseFrameTimer;
- BOOL mRunning;
-
- U64 mLastTime;
-
- struct Bucket
- {
- Bucket() :
- accum(0.0),
- endTime(0),
- lastValid(false),
- lastAccum(0.0)
- {}
-
- F64 accum;
- U64 endTime;
-
- bool lastValid;
- F64 lastAccum;
- };
-
- Bucket mBuckets[NUM_SCALES];
-
- BOOL mLastSampleValid;
- F64 mLastSampleValue;
-};
-
-class LL_COMMON_API 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 LL_COMMON_API 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();
- // used for counting the rate thorugh a point in the code
-};
-
-
-class LL_COMMON_API LLStatTime : public LLStatAccum
- // gathers statistics about time spent in a block of code
- // measure average duration per second in the block
-{
-public:
- LLStatTime( const std::string & key = "undefined" );
-
- U32 mFrameNumber; // Current frame number
- U64 mTotalTimeInFrame; // Total time (microseconds) accumulated during the last frame
-
- void setKey( const std::string & key ) { mKey = key; };
-
- virtual F32 meanValue(TimeScale scale) const;
-
-private:
- void start(); // Start and stop measuring time block
- void stop();
-
- std::string mKey; // Tag representing this time block
-
-#if LL_DEBUG
- BOOL mRunning; // TRUE if start() has been called
-#endif
-
- friend class LLPerfBlock;
-};
-
-// ----------------------------------------------------------------------------
-
-
-// Use this class on the stack to record statistics about an area of code
-class LL_COMMON_API LLPerfBlock
-{
-public:
- struct StatEntry
- {
- StatEntry(const std::string& key) : mStat(LLStatTime(key)), mCount(0) {}
- LLStatTime mStat;
- U32 mCount;
- };
- typedef std::map<std::string, StatEntry*> stat_map_t;
-
- // Use this constructor for pre-defined LLStatTime objects
- LLPerfBlock(LLStatTime* stat);
-
- // Use this constructor for normal, optional LLPerfBlock time slices
- LLPerfBlock( const char* key );
-
- // Use this constructor for dynamically created LLPerfBlock time slices
- // that are only enabled by specific control flags
- LLPerfBlock( const char* key1, const char* key2, S32 flags = LLSTATS_BASIC_STATS );
-
- ~LLPerfBlock();
-
- enum
- { // Stats bitfield flags
- LLSTATS_NO_OPTIONAL_STATS = 0x00, // No optional stats gathering, just pre-defined LLStatTime objects
- LLSTATS_BASIC_STATS = 0x01, // Gather basic optional runtime stats
- LLSTATS_SCRIPT_FUNCTIONS = 0x02, // Include LSL function calls
- };
- static void setStatsFlags( S32 flags ) { sStatsFlags = flags; };
- static S32 getStatsFlags() { return sStatsFlags; };
-
- static void clearDynamicStats(); // Reset maps to clear out dynamic objects
- static void addStatsToLLSDandReset( LLSD & stats, // Get current information and clear time bin
- LLStatAccum::TimeScale scale );
-
-private:
- // Initialize dynamically created LLStatTime objects
- void initDynamicStat(const std::string& key);
-
- std::string mLastPath; // Save sCurrentStatPath when this is called
- LLStatTime * mPredefinedStat; // LLStatTime object to get data
- StatEntry * mDynamicStat; // StatEntryobject to get data
-
- static S32 sStatsFlags; // Control what is being recorded
- static stat_map_t sStatMap; // Map full path string to LLStatTime objects
- static std::string sCurrentStatPath; // Something like "frame/physics/physics step"
-};
-
-// ----------------------------------------------------------------------------
-
-class LL_COMMON_API LLPerfStats
-{
-public:
- LLPerfStats(const std::string& process_name = "unknown", S32 process_pid = 0);
- virtual ~LLPerfStats();
-
- virtual void init(); // Reset and start all stat timers
- virtual void updatePerFrameStats();
- // Override these function to add process-specific information to the performance log header and per-frame logging.
- virtual void addProcessHeaderInfo(LLSD& info) { /* not implemented */ }
- virtual void addProcessFrameInfo(LLSD& info, LLStatAccum::TimeScale scale) { /* not implemented */ }
-
- // High-resolution frame stats
- BOOL frameStatsIsRunning() { return (mReportPerformanceStatEnd > 0.); };
- F32 getReportPerformanceInterval() const { return mReportPerformanceStatInterval; };
- void setReportPerformanceInterval( F32 interval ) { mReportPerformanceStatInterval = interval; };
- void setReportPerformanceDuration( F32 seconds, S32 flags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS );
- void setProcessName(const std::string& process_name) { mProcessName = process_name; }
- void setProcessPID(S32 process_pid) { mProcessPID = process_pid; }
-
-protected:
- void openPerfStatsFile(); // Open file for high resolution metrics logging
- void dumpIntervalPerformanceStats();
-
- llofstream mFrameStatsFile; // File for per-frame stats
- BOOL mFrameStatsFileFailure; // Flag to prevent repeat opening attempts
- BOOL mSkipFirstFrameStats; // Flag to skip one (partial) frame report
- std::string mProcessName;
- S32 mProcessPID;
-
-private:
- F32 mReportPerformanceStatInterval; // Seconds between performance stats
- F64 mReportPerformanceStatEnd; // End time (seconds) for performance stats
-};
-
-// ----------------------------------------------------------------------------
-class LL_COMMON_API LLStat
-{
-private:
- typedef std::multimap<std::string, LLStat*> stat_map_t;
- static stat_map_t sStatList;
-
- void init();
-
-public:
- LLStat(U32 num_bins = 32, BOOL use_frame_timer = FALSE);
- LLStat(std::string name, 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;
-
- std::string mName;
-
- static LLTimer sTimer;
- static LLFrameTimer sFrameTimer;
-
-public:
- static LLStat* getStat(const std::string& name)
- {
- // return the first stat that matches 'name'
- stat_map_t::iterator iter = sStatList.find(name);
- if (iter != sStatList.end())
- return iter->second;
- else
- return NULL;
- }
-};
-
-#endif // LL_STAT_
diff --git a/indra/llcommon/llstatenums.h b/indra/llcommon/llstatenums.h
index f8bf07f19e..6515a3e3cb 100644
--- a/indra/llcommon/llstatenums.h
+++ b/indra/llcommon/llstatenums.h
@@ -1,74 +1,31 @@
/**
* @file llstatenums.h
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLSTATENUMS_H
#define LL_LLSTATENUMS_H
-enum
-{
- LL_SIM_STAT_TIME_DILATION, // 0
- LL_SIM_STAT_FPS,
- LL_SIM_STAT_PHYSFPS,
- LL_SIM_STAT_AGENTUPS,
- LL_SIM_STAT_FRAMEMS,
- LL_SIM_STAT_NETMS, // 5
- LL_SIM_STAT_SIMOTHERMS,
- LL_SIM_STAT_SIMPHYSICSMS,
- LL_SIM_STAT_AGENTMS,
- LL_SIM_STAT_IMAGESMS,
- LL_SIM_STAT_SCRIPTMS, // 10
- LL_SIM_STAT_NUMTASKS,
- LL_SIM_STAT_NUMTASKSACTIVE,
- LL_SIM_STAT_NUMAGENTMAIN,
- LL_SIM_STAT_NUMAGENTCHILD,
- LL_SIM_STAT_NUMSCRIPTSACTIVE, // 15
- LL_SIM_STAT_LSLIPS,
- LL_SIM_STAT_INPPS,
- LL_SIM_STAT_OUTPPS,
- LL_SIM_STAT_PENDING_DOWNLOADS,
- LL_SIM_STAT_PENDING_UPLOADS, // 20
- LL_SIM_STAT_VIRTUAL_SIZE_KB,
- LL_SIM_STAT_RESIDENT_SIZE_KB,
- LL_SIM_STAT_PENDING_LOCAL_UPLOADS,
- LL_SIM_STAT_TOTAL_UNACKED_BYTES,
- LL_SIM_STAT_PHYSICS_PINNED_TASKS, // 25
- LL_SIM_STAT_PHYSICS_LOD_TASKS,
- LL_SIM_STAT_SIMPHYSICSSTEPMS,
- LL_SIM_STAT_SIMPHYSICSSHAPEMS,
- LL_SIM_STAT_SIMPHYSICSOTHERMS,
- LL_SIM_STAT_SIMPHYSICSMEMORY, // 30
- LL_SIM_STAT_SCRIPT_EPS,
- LL_SIM_STAT_SIMSPARETIME,
- LL_SIM_STAT_SIMSLEEPTIME,
- LL_SIM_STAT_IOPUMPTIME,
-};
+
#endif
diff --git a/indra/llcommon/llstaticstringtable.h b/indra/llcommon/llstaticstringtable.h
new file mode 100644
index 0000000000..d7e0e8a08d
--- /dev/null
+++ b/indra/llcommon/llstaticstringtable.h
@@ -0,0 +1,82 @@
+/**
+ * @file llstringtable.h
+ * @brief The LLStringTable class provides a _fast_ method for finding
+ * unique copies of strings.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_STATIC_STRING_TABLE_H
+#define LL_STATIC_STRING_TABLE_H
+
+#include "lldefs.h"
+#include <boost/unordered_map.hpp>
+#include "llstl.h"
+
+class LLStaticHashedString
+{
+public:
+
+ LLStaticHashedString(const std::string& s)
+ {
+ string_hash = makehash(s);
+ string = s;
+ }
+
+ const std::string& String() const { return string; }
+ size_t Hash() const { return string_hash; }
+
+ bool operator==(const LLStaticHashedString& b) const { return Hash() == b.Hash(); }
+
+protected:
+
+ size_t makehash(const std::string& s)
+ {
+ size_t len = s.size();
+ const char* c = s.c_str();
+ size_t hashval = 0;
+ for (size_t i=0; i<len; i++)
+ {
+ hashval = ((hashval<<5) + hashval) + *c++;
+ }
+ return hashval;
+ }
+
+ std::string string;
+ size_t string_hash;
+};
+
+struct LLStaticStringHasher
+{
+ enum { bucket_size = 8 };
+ size_t operator()(const LLStaticHashedString& key_value) const { return key_value.Hash(); }
+ bool operator()(const LLStaticHashedString& left, const LLStaticHashedString& right) const { return left.Hash() < right.Hash(); }
+};
+
+template< typename MappedObject >
+class LL_COMMON_API LLStaticStringTable
+ : public boost::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher >
+{
+};
+
+#endif
+
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
index 788617953e..02f10fa2ba 100644..100755
--- a/indra/llcommon/llstl.h
+++ b/indra/llcommon/llstl.h
@@ -2,31 +2,25 @@
* @file llstl.h
* @brief helper object & functions for use with the stl.
*
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,8 +31,9 @@
#include <algorithm>
#include <map>
#include <vector>
+#include <list>
#include <set>
-#include <deque>
+#include <typeinfo>
// 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;
@@ -103,6 +98,7 @@ struct DeletePointerArray
// The general form is:
//
// std::for_each(somemap.begin(), somemap.end(), DeletePairedPointer());
+// somemap.clear(); // Don't leave dangling pointers around
struct DeletePairedPointer
{
@@ -129,7 +125,7 @@ struct DeletePairedPointerArray
// compiler, the second unary_function template parameter can be set
// to void.
//
-// Here's a snippit showing how you use this object:
+// Here's a snippet showing how you use this object:
//
// typedef std::map<int, widget*> map_type;
// map_type widget_map;
@@ -174,6 +170,49 @@ struct CopyNewPointer
}
};
+template<typename T, typename ALLOC>
+void delete_and_clear(std::list<T*, ALLOC>& list)
+{
+ std::for_each(list.begin(), list.end(), DeletePointer());
+ list.clear();
+}
+
+template<typename T, typename ALLOC>
+void delete_and_clear(std::vector<T*, ALLOC>& vector)
+{
+ std::for_each(vector.begin(), vector.end(), DeletePointer());
+ vector.clear();
+}
+
+template<typename T, typename COMPARE, typename ALLOC>
+void delete_and_clear(std::set<T*, COMPARE, ALLOC>& set)
+{
+ std::for_each(set.begin(), set.end(), DeletePointer());
+ set.clear();
+}
+
+template<typename K, typename V, typename COMPARE, typename ALLOC>
+void delete_and_clear(std::map<K, V*, COMPARE, ALLOC>& map)
+{
+ std::for_each(map.begin(), map.end(), DeletePairedPointer());
+ map.clear();
+}
+
+template<typename T>
+void delete_and_clear(T*& ptr)
+{
+ delete ptr;
+ ptr = NULL;
+}
+
+
+template<typename T>
+void delete_and_clear_array(T*& ptr)
+{
+ delete[] ptr;
+ ptr = NULL;
+}
+
// Simple function to help with finding pointers in maps.
// For example:
// typedef map_t;
@@ -233,7 +272,6 @@ inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value)
}
};
-// Useful for replacing the removeObj() functionality of LLDynamicArray
// Example:
// for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); )
// {
@@ -242,8 +280,8 @@ inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value)
// else
// ++iter;
// }
-template <typename T, typename Iter>
-inline Iter vector_replace_with_last(std::vector<T>& invec, Iter iter)
+template <typename T>
+inline typename std::vector<T>::iterator vector_replace_with_last(std::vector<T>& invec, typename std::vector<T>::iterator iter)
{
typename std::vector<T>::iterator last = invec.end(); --last;
if (iter == invec.end())
@@ -263,7 +301,6 @@ inline Iter vector_replace_with_last(std::vector<T>& invec, Iter iter)
}
};
-// Useful for replacing the removeObj() functionality of LLDynamicArray
// Example:
// vector_replace_with_last(mList, x);
template <typename T>
@@ -476,4 +513,201 @@ llbind2nd(const _Operation& __oper, const _Tp& __x)
return llbinder2nd<_Operation>(__oper, _Arg2_type(__x));
}
+/**
+ * Compare std::type_info* pointers a la std::less. We break this out as a
+ * separate function for use in two different std::less specializations.
+ */
+inline
+bool before(const std::type_info* lhs, const std::type_info* rhs)
+{
+#if LL_LINUX && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
+ // If we're building on Linux with gcc, and it's either gcc 3.x or
+ // 4.{0,1,2,3}, then we have to use a workaround. Note that we use gcc on
+ // Mac too, and some people build with gcc on Windows (cygwin or mingw).
+ // On Linux, different load modules may produce different type_info*
+ // pointers for the same type. Have to compare name strings to get good
+ // results.
+ return strcmp(lhs->name(), rhs->name()) < 0;
+#else // not Linux, or gcc 4.4+
+ // Just use before(), as we normally would
+ return lhs->before(*rhs);
+#endif
+}
+
+/**
+ * Specialize std::less<std::type_info*> to use std::type_info::before().
+ * See MAINT-1175. It is NEVER a good idea to directly compare std::type_info*
+ * because, on Linux, you might get different std::type_info* pointers for the
+ * same type (from different load modules)!
+ */
+namespace std
+{
+ template <>
+ struct less<const std::type_info*>:
+ public std::binary_function<const std::type_info*, const std::type_info*, bool>
+ {
+ bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
+ {
+ return before(lhs, rhs);
+ }
+ };
+
+ template <>
+ struct less<std::type_info*>:
+ public std::binary_function<std::type_info*, std::type_info*, bool>
+ {
+ bool operator()(std::type_info* lhs, std::type_info* rhs) const
+ {
+ return before(lhs, rhs);
+ }
+ };
+} // std
+
+
+/**
+ * Implementation for ll_template_cast() (q.v.).
+ *
+ * Default implementation: trying to cast two completely unrelated types
+ * returns 0. Typically you'd specify T and U as pointer types, but in fact T
+ * can be any type that can be initialized with 0.
+ */
+template <typename T, typename U>
+struct ll_template_cast_impl
+{
+ T operator()(U)
+ {
+ return 0;
+ }
+};
+
+/**
+ * ll_template_cast<T>(some_value) is for use in a template function when
+ * some_value might be of arbitrary type, but you want to recognize type T
+ * specially.
+ *
+ * It's designed for use with pointer types. Example:
+ * @code
+ * struct SpecialClass
+ * {
+ * void someMethod(const std::string&) const;
+ * };
+ *
+ * template <class REALCLASS>
+ * void somefunc(const REALCLASS& instance)
+ * {
+ * const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
+ * if (ptr)
+ * {
+ * ptr->someMethod("Call method only available on SpecialClass");
+ * }
+ * }
+ * @endcode
+ *
+ * Why is this better than dynamic_cast<>? Because unless OtherClass is
+ * polymorphic, the following won't even compile (gcc 4.0.1):
+ * @code
+ * OtherClass other;
+ * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
+ * @endcode
+ * to say nothing of this:
+ * @code
+ * void function(int);
+ * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
+ * @endcode
+ * ll_template_cast handles these kinds of cases by returning 0.
+ */
+template <typename T, typename U>
+T ll_template_cast(U value)
+{
+ return ll_template_cast_impl<T, U>()(value);
+}
+
+/**
+ * Implementation for ll_template_cast() (q.v.).
+ *
+ * Implementation for identical types: return same value.
+ */
+template <typename T>
+struct ll_template_cast_impl<T, T>
+{
+ T operator()(T value)
+ {
+ return value;
+ }
+};
+
+/**
+ * LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
+ * type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
+ * presuming that @c source can be converted to @c dest by the normal rules of
+ * C++.
+ *
+ * By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
+ * type is literally identical to @c dest. (This is because of the
+ * straightforward application of template specialization rules.) That can
+ * lead to surprising results, e.g.:
+ *
+ * @code
+ * Foo myFoo;
+ * const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
+ * @endcode
+ *
+ * Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
+ * -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
+ * force the compiler to do the right thing.)
+ *
+ * More disappointingly:
+ * @code
+ * struct Base {};
+ * struct Subclass: public Base {};
+ * Subclass object;
+ * Base* ptr = ll_template_cast<Base*>(&object);
+ * @endcode
+ *
+ * Here @c ptr will be 0 because <tt>&object</tt> is of type
+ * <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
+ * succeed, but without our help ll_template_cast can't recognize it.
+ *
+ * The following would suffice:
+ * @code
+ * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
+ * ...
+ * Base* ptr = ll_template_cast<Base*>(&object);
+ * @endcode
+ *
+ * However, as noted earlier, this is easily fooled:
+ * @code
+ * const Base* ptr = ll_template_cast<const Base*>(&object);
+ * @endcode
+ * would still produce 0 because we haven't yet seen:
+ * @code
+ * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
+ * @endcode
+ *
+ * @TODO
+ * This macro should use Boost type_traits facilities for stripping and
+ * re-adding @c const and @c volatile qualifiers so that invoking
+ * LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
+ * permitted permutations. It's really not fair to the coder to require
+ * separate:
+ * @code
+ * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
+ * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
+ * LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
+ * @endcode
+ *
+ * (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
+ * because that's not permitted by normal C++ assignment anyway.)
+ */
+#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
+template <> \
+struct ll_template_cast_impl<DEST, SOURCE> \
+{ \
+ DEST operator()(SOURCE wrapper) \
+ { \
+ return wrapper; \
+ } \
+}
+
+
#endif // LL_LLSTL_H
diff --git a/indra/llcommon/llstreamqueue.cpp b/indra/llcommon/llstreamqueue.cpp
new file mode 100755
index 0000000000..1116a2b6a2
--- /dev/null
+++ b/indra/llcommon/llstreamqueue.cpp
@@ -0,0 +1,24 @@
+/**
+ * @file llstreamqueue.cpp
+ * @author Nat Goodspeed
+ * @date 2012-01-05
+ * @brief Implementation for llstreamqueue.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llstreamqueue.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+
+// As of this writing, llstreamqueue.h is entirely template-based, therefore
+// we don't strictly need a corresponding .cpp file. However, our CMake test
+// macro assumes one. Here it is.
+bool llstreamqueue_cpp_ignored = true;
diff --git a/indra/llcommon/llstreamqueue.h b/indra/llcommon/llstreamqueue.h
new file mode 100755
index 0000000000..0726bad175
--- /dev/null
+++ b/indra/llcommon/llstreamqueue.h
@@ -0,0 +1,240 @@
+/**
+ * @file llstreamqueue.h
+ * @author Nat Goodspeed
+ * @date 2012-01-04
+ * @brief Definition of LLStreamQueue
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLSTREAMQUEUE_H)
+#define LL_LLSTREAMQUEUE_H
+
+#include <string>
+#include <list>
+#include <iosfwd> // std::streamsize
+#include <boost/iostreams/categories.hpp>
+
+/**
+ * This class is a growable buffer between a producer and consumer. It serves
+ * as a queue usable with Boost.Iostreams -- hence, a "stream queue."
+ *
+ * This is especially useful for buffering nonblocking I/O. For instance, we
+ * want application logic to be able to serialize LLSD to a std::ostream. We
+ * may write more data than the destination pipe can handle all at once, but
+ * it's imperative NOT to block the application-level serialization call. So
+ * we buffer it instead. Successive frames can try nonblocking writes to the
+ * destination pipe until all buffered data has been sent.
+ *
+ * Similarly, we want application logic be able to deserialize LLSD from a
+ * std::istream. Again, we must not block that deserialize call waiting for
+ * more data to arrive from the input pipe! Instead we build up a buffer over
+ * a number of frames, using successive nonblocking reads, until we have
+ * "enough" data to be able to present it through a std::istream.
+ *
+ * @note The use cases for this class overlap somewhat with those for the
+ * LLIOPipe/LLPumpIO hierarchies, and indeed we considered using those. This
+ * class has two virtues over the older machinery:
+ *
+ * # It's vastly simpler -- way fewer concepts. It's not clear to me whether
+ * there were ever LLIOPipe/etc. use cases that demanded all the fanciness
+ * rolled in, or whether they were simply overdesigned. In any case, no
+ * remaining Lindens will admit to familiarity with those classes -- and
+ * they're sufficiently obtuse that it would take considerable learning
+ * curve to figure out how to use them properly. The bottom line is that
+ * current management is not keen on any more engineers climbing that curve.
+ * # This class is designed around available components such as std::string,
+ * std::list, Boost.Iostreams. There's less proprietary code.
+ */
+template <typename Ch>
+class LLGenericStreamQueue
+{
+public:
+ LLGenericStreamQueue():
+ mSize(0),
+ mClosed(false)
+ {}
+
+ /**
+ * Boost.Iostreams Source Device facade for use with other Boost.Iostreams
+ * functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
+ * 1.48 Iostreams concepts; instead it behaves as both a Sink and a
+ * Source. This is its Source facade.
+ */
+ struct Source
+ {
+ typedef Ch char_type;
+ typedef boost::iostreams::source_tag category;
+
+ /// Bind the underlying LLGenericStreamQueue
+ Source(LLGenericStreamQueue& sq):
+ mStreamQueue(sq)
+ {}
+
+ // Read up to n characters from the underlying data source into the
+ // buffer s, returning the number of characters read; return -1 to
+ // indicate EOF
+ std::streamsize read(Ch* s, std::streamsize n)
+ {
+ return mStreamQueue.read(s, n);
+ }
+
+ LLGenericStreamQueue& mStreamQueue;
+ };
+
+ /**
+ * Boost.Iostreams Sink Device facade for use with other Boost.Iostreams
+ * functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
+ * 1.48 Iostreams concepts; instead it behaves as both a Sink and a
+ * Source. This is its Sink facade.
+ */
+ struct Sink
+ {
+ typedef Ch char_type;
+ typedef boost::iostreams::sink_tag category;
+
+ /// Bind the underlying LLGenericStreamQueue
+ Sink(LLGenericStreamQueue& sq):
+ mStreamQueue(sq)
+ {}
+
+ /// Write up to n characters from the buffer s to the output sequence,
+ /// returning the number of characters written
+ std::streamsize write(const Ch* s, std::streamsize n)
+ {
+ return mStreamQueue.write(s, n);
+ }
+
+ /// Send EOF to consumer
+ void close()
+ {
+ mStreamQueue.close();
+ }
+
+ LLGenericStreamQueue& mStreamQueue;
+ };
+
+ /// Present Boost.Iostreams Source facade
+ Source asSource() { return Source(*this); }
+ /// Present Boost.Iostreams Sink facade
+ Sink asSink() { return Sink(*this); }
+
+ /// append data to buffer
+ std::streamsize write(const Ch* s, std::streamsize n)
+ {
+ // Unclear how often we might be asked to write 0 bytes -- perhaps a
+ // naive caller responding to an unready nonblocking read. But if we
+ // do get such a call, don't add a completely empty BufferList entry.
+ if (n == 0)
+ return n;
+ // We could implement this using a single std::string object, a la
+ // ostringstream. But the trouble with appending to a string is that
+ // you might have to recopy all previous contents to grow its size. If
+ // we want this to scale to large data volumes, better to allocate
+ // individual pieces.
+ mBuffer.push_back(string(s, n));
+ mSize += n;
+ return n;
+ }
+
+ /**
+ * Inform this LLGenericStreamQueue that no further data are forthcoming.
+ * For our purposes, close() is strictly a producer-side operation;
+ * there's little point in closing the consumer side.
+ */
+ void close()
+ {
+ mClosed = true;
+ }
+
+ /// consume data from buffer
+ std::streamsize read(Ch* s, std::streamsize n)
+ {
+ // read() is actually a convenience method for peek() followed by
+ // skip().
+ std::streamsize got(peek(s, n));
+ // We can only skip() as many characters as we can peek(); ignore
+ // skip() return here.
+ skip(n);
+ return got;
+ }
+
+ /// Retrieve data from buffer without consuming. Like read(), return -1 on
+ /// EOF.
+ std::streamsize peek(Ch* s, std::streamsize n) const;
+
+ /// Consume data from buffer without retrieving. Unlike read() and peek(),
+ /// at EOF we simply skip 0 characters.
+ std::streamsize skip(std::streamsize n);
+
+ /// How many characters do we currently have buffered?
+ std::streamsize size() const
+ {
+ return mSize;
+ }
+
+private:
+ typedef std::basic_string<Ch> string;
+ typedef std::list<string> BufferList;
+ BufferList mBuffer;
+ std::streamsize mSize;
+ bool mClosed;
+};
+
+template <typename Ch>
+std::streamsize LLGenericStreamQueue<Ch>::peek(Ch* s, std::streamsize n) const
+{
+ // Here we may have to build up 'n' characters from an arbitrary
+ // number of individual BufferList entries.
+ typename BufferList::const_iterator bli(mBuffer.begin()), blend(mBuffer.end());
+ // Indicate EOF if producer has closed the pipe AND we've exhausted
+ // all previously-buffered data.
+ if (mClosed && bli == blend)
+ {
+ return -1;
+ }
+ // Here either producer hasn't yet closed, or we haven't yet exhausted
+ // remaining data.
+ std::streamsize needed(n), got(0);
+ // Loop until either we run out of BufferList entries or we've
+ // completely satisfied the request.
+ for ( ; bli != blend && needed; ++bli)
+ {
+ std::streamsize chunk(std::min(needed, std::streamsize(bli->length())));
+ std::copy(bli->begin(), bli->begin() + chunk, s);
+ needed -= chunk;
+ s += chunk;
+ got += chunk;
+ }
+ return got;
+}
+
+template <typename Ch>
+std::streamsize LLGenericStreamQueue<Ch>::skip(std::streamsize n)
+{
+ typename BufferList::iterator bli(mBuffer.begin()), blend(mBuffer.end());
+ std::streamsize toskip(n), skipped(0);
+ while (bli != blend && toskip >= bli->length())
+ {
+ std::streamsize chunk(bli->length());
+ typename BufferList::iterator zap(bli++);
+ mBuffer.erase(zap);
+ mSize -= chunk;
+ toskip -= chunk;
+ skipped += chunk;
+ }
+ if (bli != blend && toskip)
+ {
+ bli->erase(bli->begin(), bli->begin() + toskip);
+ mSize -= toskip;
+ skipped += toskip;
+ }
+ return skipped;
+}
+
+typedef LLGenericStreamQueue<char> LLStreamQueue;
+typedef LLGenericStreamQueue<wchar_t> LLWStreamQueue;
+
+#endif /* ! defined(LL_LLSTREAMQUEUE_H) */
diff --git a/indra/llcommon/llstreamtools.cpp b/indra/llcommon/llstreamtools.cpp
index 4ed2df58e0..d7a6f47932 100644..100755
--- a/indra/llcommon/llstreamtools.cpp
+++ b/indra/llcommon/llstreamtools.cpp
@@ -2,31 +2,25 @@
* @file llstreamtools.cpp
* @brief some helper functions for parsing legacy simstate and asset files.
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h
index f64e761409..1b04bf91d7 100644..100755
--- a/indra/llcommon/llstreamtools.h
+++ b/indra/llcommon/llstreamtools.h
@@ -2,31 +2,25 @@
* @file llstreamtools.h
* @brief some helper functions for parsing legacy simstate and asset files.
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h
index 369b06b48a..ed9284d2c5 100644..100755
--- a/indra/llcommon/llstrider.h
+++ b/indra/llcommon/llstrider.h
@@ -1,31 +1,25 @@
/**
* @file llstrider.h
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -50,13 +44,23 @@ public:
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;}
+ LLStrider<Object> operator+(const S32& index)
+ {
+ LLStrider<Object> ret;
+ ret.mBytep = mBytep + mSkip*index;
+ ret.mSkip = mSkip;
+
+ return ret;
+ }
+ void skip(const U32 index) { mBytep += mSkip*index;}
+ U32 getSkip() const { return mSkip; }
Object* get() { return mObjectp; }
Object* operator->() { return mObjectp; }
Object& operator *() { return *mObjectp; }
Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; }
Object* operator +=(int i) { mBytep += mSkip*i; return mObjectp; }
+
Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); }
};
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index 5f3d9d6582..f3b8999883 100644..100755
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -2,31 +2,25 @@
* @file llstring.cpp
* @brief String utility functions and the std::string class.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,15 +28,15 @@
#include "llstring.h"
#include "llerror.h"
+#include "llfasttimer.h"
+#include "llsd.h"
#if LL_WINDOWS
-#define WIN32_LEAN_AND_MEAN
-#include <winsock2.h>
-#include <windows.h>
+#include "llwin32headerslean.h"
#include <winnls.h> // for WideCharToMultiByte
#endif
-LLFastTimer::DeclareTimer FT_STRING_FORMAT("String Format");
+LLTrace::BlockTimerStatHandle FT_STRING_FORMAT("String Format");
std::string ll_safe_string(const char* in)
@@ -53,10 +47,28 @@ std::string ll_safe_string(const char* in)
std::string ll_safe_string(const char* in, S32 maxlen)
{
- if(in) return std::string(in, maxlen);
+ if(in && maxlen > 0 ) return std::string(in, maxlen);
+
return std::string();
}
+bool is_char_hex(char hex)
+{
+ if((hex >= '0') && (hex <= '9'))
+ {
+ return true;
+ }
+ else if((hex >= 'a') && (hex <='f'))
+ {
+ return true;
+ }
+ else if((hex >= 'A') && (hex <='F'))
+ {
+ return true;
+ }
+ return false; // uh - oh, not hex any more...
+}
+
U8 hex_as_nybble(char hex)
{
if((hex >= '0') && (hex <= '9'))
@@ -95,10 +107,10 @@ bool iswindividual(llwchar elem)
bool _read_file_into_string(std::string& str, const std::string& filename)
{
- llifstream ifs(filename, llifstream::binary);
+ llifstream ifs(filename.c_str(), llifstream::binary);
if (!ifs.is_open())
{
- llinfos << "Unable to open file " << filename << llendl;
+ LL_INFOS() << "Unable to open file " << filename << LL_ENDL;
return false;
}
@@ -176,7 +188,7 @@ S32 wchar_to_utf8chars(llwchar in_char, char* outchars)
}
else
{
- llwarns << "Invalid Unicode character " << cur_char << "!" << llendl;
+ LL_WARNS() << "Invalid Unicode character " << cur_char << "!" << LL_ENDL;
*outchars++ = LL_UNKNOWN_CHAR;
}
return outchars - base;
@@ -633,14 +645,14 @@ namespace snprintf_hack
}
}
-std::string ll_convert_wide_to_string(const wchar_t* in)
+std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page)
{
std::string out;
if(in)
{
int len_in = wcslen(in);
int len_out = WideCharToMultiByte(
- CP_ACP,
+ code_page,
0,
in,
len_in,
@@ -655,7 +667,7 @@ std::string ll_convert_wide_to_string(const wchar_t* in)
if(pout)
{
WideCharToMultiByte(
- CP_ACP,
+ code_page,
0,
in,
len_in,
@@ -669,6 +681,38 @@ std::string ll_convert_wide_to_string(const wchar_t* in)
}
return out;
}
+
+wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page)
+{
+ // From review:
+ // We can preallocate a wide char buffer that is the same length (in wchar_t elements) as the utf8 input,
+ // plus one for a null terminator, and be guaranteed to not overflow.
+
+ // Normally, I'd call that sort of thing premature optimization,
+ // but we *are* seeing string operations taking a bunch of time, especially when constructing widgets.
+// int output_str_len = MultiByteToWideChar(code_page, 0, in.c_str(), in.length(), NULL, 0);
+
+ // reserve place to NULL terminator
+ int output_str_len = in.length();
+ wchar_t* w_out = new wchar_t[output_str_len + 1];
+
+ memset(w_out, 0, output_str_len + 1);
+ int real_output_str_len = MultiByteToWideChar (code_page, 0, in.c_str(), in.length(), w_out, output_str_len);
+
+ //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858.
+ w_out[real_output_str_len] = 0;
+
+ return w_out;
+}
+
+std::string ll_convert_string_to_utf8_string(const std::string& in)
+{
+ wchar_t* w_mesg = ll_convert_string_to_wide(in, CP_ACP);
+ std::string out_utf8(ll_convert_wide_to_string(w_mesg, CP_UTF8));
+ delete[] w_mesg;
+
+ return out_utf8;
+}
#endif // LL_WINDOWS
long LLStringOps::sPacificTimeOffset = 0;
@@ -676,6 +720,17 @@ long LLStringOps::sLocalTimeOffset = 0;
bool LLStringOps::sPacificDaylightTime = 0;
std::map<std::string, std::string> LLStringOps::datetimeToCodes;
+std::vector<std::string> LLStringOps::sWeekDayList;
+std::vector<std::string> LLStringOps::sWeekDayShortList;
+std::vector<std::string> LLStringOps::sMonthList;
+std::vector<std::string> LLStringOps::sMonthShortList;
+
+
+std::string LLStringOps::sDayFormat;
+std::string LLStringOps::sAM;
+std::string LLStringOps::sPM;
+
+
S32 LLStringOps::collate(const llwchar* a, const llwchar* b)
{
#if LL_WINDOWS
@@ -694,14 +749,17 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
nowT = time (NULL);
- tmpT = localtime (&nowT);
- localT = mktime (tmpT);
-
tmpT = gmtime (&nowT);
gmtT = mktime (tmpT);
+ tmpT = localtime (&nowT);
+ localT = mktime (tmpT);
+
sLocalTimeOffset = (long) (gmtT - localT);
-
+ if (tmpT->tm_isdst)
+ {
+ sLocalTimeOffset -= 60 * 60; // 1 hour
+ }
sPacificDaylightTime = daylight;
sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60;
@@ -715,6 +773,7 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
datetimeToCodes["month"] = "%B"; // August
datetimeToCodes["mthnum"] = "%m"; // 08
datetimeToCodes["day"] = "%d"; // 31
+ datetimeToCodes["sday"] = "%-d"; // 9
datetimeToCodes["hour24"] = "%H"; // 14
datetimeToCodes["hour"] = "%H"; // 14
datetimeToCodes["hour12"] = "%I"; // 02
@@ -724,6 +783,50 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
datetimeToCodes["timezone"] = "%Z"; // PST
}
+void tokenizeStringToArray(const std::string& data, std::vector<std::string>& output)
+{
+ output.clear();
+ size_t length = data.size();
+
+ // tokenize it and put it in the array
+ std::string cur_word;
+ for(size_t i = 0; i < length; ++i)
+ {
+ if(data[i] == ':')
+ {
+ output.push_back(cur_word);
+ cur_word.clear();
+ }
+ else
+ {
+ cur_word.append(1, data[i]);
+ }
+ }
+ output.push_back(cur_word);
+}
+
+void LLStringOps::setupWeekDaysNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sWeekDayList);
+}
+void LLStringOps::setupWeekDaysShortNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sWeekDayShortList);
+}
+void LLStringOps::setupMonthNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sMonthList);
+}
+void LLStringOps::setupMonthShortNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sMonthShortList);
+}
+void LLStringOps::setupDayFormat(const std::string& data)
+{
+ sDayFormat = data;
+}
+
+
std::string LLStringOps::getDatetimeCode (std::string key)
{
std::map<std::string, std::string>::iterator iter;
@@ -819,26 +922,32 @@ namespace LLStringFn
////////////////////////////////////////////////////////////
+// Forward specialization of LLStringUtil::format before use in LLStringUtil::formatDatetime.
+template<>
+S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions);
+
//static
template<>
void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims)
{
- std::string currToken;
- std::string::size_type begIdx, endIdx;
-
- begIdx = instr.find_first_not_of (delims);
- while (begIdx != std::string::npos)
+ // Starting at offset 0, scan forward for the next non-delimiter. We're
+ // done when the only characters left in 'instr' are delimiters.
+ for (std::string::size_type begIdx, endIdx = 0;
+ (begIdx = instr.find_first_not_of (delims, endIdx)) != std::string::npos; )
{
+ // Found a non-delimiter. After that, find the next delimiter.
endIdx = instr.find_first_of (delims, begIdx);
if (endIdx == std::string::npos)
{
+ // No more delimiters: this token extends to the end of the string.
endIdx = instr.length();
}
- currToken = instr.substr(begIdx, endIdx - begIdx);
+ // extract the token between begIdx and endIdx; substr() needs length
+ std::string currToken(instr.substr(begIdx, endIdx - begIdx));
LLStringUtil::trim (currToken);
tokens.push_back(currToken);
- begIdx = instr.find_first_not_of (delims, endIdx);
+ // next scan past delimiters starts at endIdx
}
}
@@ -847,13 +956,18 @@ LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr,
{
const std::string delims (",");
- // Find the first ]
- size_type pos2 = instr.find(']', start);
+ // Find the first [
+ size_type pos1 = instr.find('[', start);
+ if (pos1 == std::string::npos)
+ return std::string::npos;
+
+ //Find the first ] after the initial [
+ size_type pos2 = instr.find(']', pos1);
if (pos2 == std::string::npos)
return std::string::npos;
- // Find the last [ before ]
- size_type pos1 = instr.find_last_of('[', pos2-1);
+ // Find the last [ before ] in case of nested [[]]
+ pos1 = instr.find_last_of('[', pos2-1);
if (pos1 == std::string::npos || pos1 < start)
return std::string::npos;
@@ -911,6 +1025,20 @@ bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token
return false;
}
+//static
+template<>
+void LLStringUtil::setLocale(std::string inLocale)
+{
+ sLocale = inLocale;
+};
+
+//static
+template<>
+std::string LLStringUtil::getLocale(void)
+{
+ return sLocale;
+};
+
// static
template<>
void LLStringUtil::formatNumber(std::string& numStr, std::string decimals)
@@ -921,7 +1049,14 @@ void LLStringUtil::formatNumber(std::string& numStr, std::string decimals)
convertToS32 (decimals, intDecimals);
if (!sLocale.empty())
{
- strStream.imbue (std::locale(sLocale.c_str()));
+ // std::locale() throws if the locale is unknown! (EXT-7926)
+ try
+ {
+ strStream.imbue(std::locale(sLocale.c_str()));
+ } catch (const std::exception &)
+ {
+ LL_WARNS_ONCE("Locale") << "Cannot set locale to " << sLocale << LL_ENDL;
+ }
}
if (!intDecimals)
@@ -984,7 +1119,58 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
}
return true;
}
- replacement = datetime.toHTTPDateString(code);
+
+ //EXT-7013
+ //few codes are not suppotred by strtime function (example - weekdays for Japanise)
+ //so use predefined ones
+
+ //if sWeekDayList is not empty than current locale doesn't support
+ //weekday name.
+ time_t loc_seconds = (time_t) secFromEpoch;
+ if(LLStringOps::sWeekDayList.size() == 7 && code == "%A")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = LLStringOps::sWeekDayList[gmt->tm_wday];
+ }
+ else if(LLStringOps::sWeekDayShortList.size() == 7 && code == "%a")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = LLStringOps::sWeekDayShortList[gmt->tm_wday];
+ }
+ else if(LLStringOps::sMonthList.size() == 12 && code == "%B")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = LLStringOps::sMonthList[gmt->tm_mon];
+ }
+ else if( !LLStringOps::sDayFormat.empty() && code == "%d" )
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ LLStringUtil::format_map_t args;
+ args["[MDAY]"] = llformat ("%d", gmt->tm_mday);
+ replacement = LLStringOps::sDayFormat;
+ LLStringUtil::format(replacement, args);
+ }
+ else if (code == "%-d")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = llformat ("%d", gmt->tm_mday); // day of the month without leading zero
+ }
+ else if( !LLStringOps::sAM.empty() && !LLStringOps::sPM.empty() && code == "%p" )
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ if(gmt->tm_hour<12)
+ {
+ replacement = LLStringOps::sAM;
+ }
+ else
+ {
+ replacement = LLStringOps::sPM;
+ }
+ }
+ else
+ {
+ replacement = datetime.toHTTPDateString(code);
+ }
// *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format
// to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738).
@@ -1009,7 +1195,7 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
template<>
S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
{
- LLFastTimer ft(FT_STRING_FORMAT);
+ LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT);
S32 res = 0;
std::string output;
@@ -1082,7 +1268,7 @@ S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
template<>
S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
{
- LLFastTimer ft(FT_STRING_FORMAT);
+ LL_RECORD_BLOCK_TIME(FT_STRING_FORMAT);
S32 res = 0;
if (!substitutions.isMap())
@@ -1211,7 +1397,7 @@ void LLStringUtilBase<T>::testHarness()
s2.erase( 4, 1 );
llassert( s2 == "hell");
- s2.insert( 0, 'y' );
+ s2.insert( 0, "y" );
llassert( s2 == "yhell");
s2.erase( 1, 3 );
llassert( s2 == "yl");
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index 62cedcde4e..0177f48bf5 100644..100755
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -2,31 +2,25 @@
* @file llstring.h
* @brief String utility functions and std::string class.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,10 +29,12 @@
#include <string>
#include <cstdio>
-#include <locale>
+//#include <locale>
#include <iomanip>
-#include "llsd.h"
-#include "llfasttimer.h"
+#include <algorithm>
+#include <vector>
+#include <map>
+#include "llformat.h"
#if LL_LINUX || LL_SOLARIS
#include <wctype.h>
@@ -46,6 +42,7 @@
#endif
#include <string.h>
+#include <boost/scoped_ptr.hpp>
#if LL_SOLARIS
// stricmp and strnicmp do not exist on Solaris:
@@ -54,6 +51,7 @@
#endif
const char LL_UNKNOWN_CHAR = '?';
+class LLSD;
#if LL_DARWIN || LL_LINUX || LL_SOLARIS
// Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already)
@@ -154,9 +152,19 @@ private:
static long sPacificTimeOffset;
static long sLocalTimeOffset;
static bool sPacificDaylightTime;
+
static std::map<std::string, std::string> datetimeToCodes;
public:
+ static std::vector<std::string> sWeekDayList;
+ static std::vector<std::string> sWeekDayShortList;
+ static std::vector<std::string> sMonthList;
+ static std::vector<std::string> sMonthShortList;
+ static std::string sDayFormat;
+
+ static std::string sAM;
+ static std::string sPM;
+
static char toUpper(char elem) { return toupper((unsigned char)elem); }
static llwchar toUpper(llwchar elem) { return towupper(elem); }
@@ -178,6 +186,9 @@ public:
static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; }
static bool isPunct(llwchar a) { return iswpunct(a) != 0; }
+ static bool isAlpha(char a) { return isalpha((unsigned char)a) != 0; }
+ static bool isAlpha(llwchar a) { return iswalpha(a) != 0; }
+
static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }
@@ -185,6 +196,14 @@ public:
static S32 collate(const llwchar* a, const llwchar* b);
static void setupDatetimeInfo(bool pacific_daylight_time);
+
+ static void setupWeekDaysNames(const std::string& data);
+ static void setupWeekDaysShortNames(const std::string& data);
+ static void setupMonthNames(const std::string& data);
+ static void setupMonthShortNames(const std::string& data);
+ static void setupDayFormat(const std::string& data);
+
+
static long getPacificTimeOffset(void) { return sPacificTimeOffset;}
static long getLocalTimeOffset(void) { return sLocalTimeOffset;}
// Is the Pacific time zone (aka server time zone)
@@ -225,40 +244,77 @@ private:
static std::string sLocale;
public:
- typedef typename std::basic_string<T>::size_type size_type;
+ typedef std::basic_string<T> string_type;
+ typedef typename string_type::size_type size_type;
public:
/////////////////////////////////////////////////////////////////////////////////////////
// Static Utility functions that operate on std::strings
- static std::basic_string<T> null;
+ static const string_type null;
typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
- LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
- LL_COMMON_API static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
- LL_COMMON_API static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
- LL_COMMON_API static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
- LL_COMMON_API static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
- LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
- LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
- static void setLocale (std::string inLocale) {sLocale = inLocale;};
- static std::string getLocale (void) {return sLocale;};
+ /// considers any sequence of delims as a single field separator
+ LL_COMMON_API static void getTokens(const string_type& instr,
+ std::vector<string_type >& tokens,
+ const string_type& delims);
+ /// like simple scan overload, but returns scanned vector
+ static std::vector<string_type> getTokens(const string_type& instr,
+ const string_type& delims);
+ /// add support for keep_delims and quotes (either could be empty string)
+ static void getTokens(const string_type& instr,
+ std::vector<string_type>& tokens,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes=string_type());
+ /// like keep_delims-and-quotes overload, but returns scanned vector
+ static std::vector<string_type> getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes=string_type());
+ /// add support for escapes (could be empty string)
+ static void getTokens(const string_type& instr,
+ std::vector<string_type>& tokens,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes,
+ const string_type& escapes);
+ /// like escapes overload, but returns scanned vector
+ static std::vector<string_type> getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes,
+ const string_type& escapes);
+
+ LL_COMMON_API static void formatNumber(string_type& numStr, string_type decimals);
+ LL_COMMON_API static bool formatDatetime(string_type& replacement, string_type token, string_type param, S32 secFromEpoch);
+ LL_COMMON_API static S32 format(string_type& s, const format_map_t& substitutions);
+ LL_COMMON_API static S32 format(string_type& s, const LLSD& substitutions);
+ LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const format_map_t& substitutions);
+ LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const LLSD& substitutions);
+ LL_COMMON_API static void setLocale (std::string inLocale);
+ LL_COMMON_API static std::string getLocale (void);
- static bool isValidIndex(const std::basic_string<T>& string, size_type i)
+ static bool isValidIndex(const string_type& 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 bool contains(const string_type& string, T c, size_type i=0)
+ {
+ return string.find(c, i) != string_type::npos;
+ }
+
+ static void trimHead(string_type& string);
+ static void trimTail(string_type& string);
+ static void trim(string_type& string) { trimHead(string); trimTail(string); }
+ static void truncate(string_type& string, size_type count);
- static void toUpper(std::basic_string<T>& string);
- static void toLower(std::basic_string<T>& string);
+ static void toUpper(string_type& string);
+ static void toLower(string_type& string);
// True if this is the head of s.
- static BOOL isHead( const std::basic_string<T>& string, const T* s );
+ static BOOL isHead( const string_type& string, const T* s );
/**
* @brief Returns true if string starts with substr
@@ -266,8 +322,8 @@ public:
* If etither string or substr are empty, this method returns false.
*/
static bool startsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr);
+ const string_type& string,
+ const string_type& substr);
/**
* @brief Returns true if string ends in substr
@@ -275,19 +331,32 @@ public:
* If etither string or substr are empty, this method returns false.
*/
static bool endsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr);
+ const string_type& string,
+ const string_type& substr);
- static void addCRLF(std::basic_string<T>& string);
- static void removeCRLF(std::basic_string<T>& string);
+ static void addCRLF(string_type& string);
+ static void removeCRLF(string_type& 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 void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement );
+ static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( string_type& string, T replacement );
+ static void replaceChar( string_type& string, T target, T replacement );
+ static void replaceString( string_type& string, string_type target, string_type replacement );
- static BOOL containsNonprintable(const std::basic_string<T>& string);
- static void stripNonprintable(std::basic_string<T>& string);
+ static BOOL containsNonprintable(const string_type& string);
+ static void stripNonprintable(string_type& string);
+
+ /**
+ * Double-quote an argument string if needed, unless it's already
+ * double-quoted. Decide whether it's needed based on the presence of any
+ * character in @a triggers (default space or double-quote). If we quote
+ * it, escape any embedded double-quote with the @a escape string (default
+ * backslash).
+ *
+ * Passing triggers="" means always quote, unless it's already double-quoted.
+ */
+ static string_type quote(const string_type& str,
+ const string_type& triggers=" \"",
+ const string_type& escape="\\");
/**
* @brief Unsafe way to make ascii characters. You should probably
@@ -296,18 +365,18 @@ public:
* The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII
* should work.
*/
- static void _makeASCII(std::basic_string<T>& string);
+ static void _makeASCII(string_type& string);
// 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);
+ static BOOL convertToBOOL(const string_type& string, BOOL& value);
+ static BOOL convertToU8(const string_type& string, U8& value);
+ static BOOL convertToS8(const string_type& string, S8& value);
+ static BOOL convertToS16(const string_type& string, S16& value);
+ static BOOL convertToU16(const string_type& string, U16& value);
+ static BOOL convertToU32(const string_type& string, U32& value);
+ static BOOL convertToS32(const string_type& string, S32& value);
+ static BOOL convertToF32(const string_type& string, F32& value);
+ static BOOL convertToF64(const string_type& string, F64& value);
/////////////////////////////////////////////////////////////////////////////////////////
// Utility functions for working with char*'s and strings
@@ -315,24 +384,24 @@ public:
// Like strcmp but also handles empty strings. Uses
// current locale.
static S32 compareStrings(const T* lhs, const T* rhs);
- static S32 compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+ static S32 compareStrings(const string_type& lhs, const string_type& 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);
- static S32 compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+ static S32 compareInsensitive(const string_type& lhs, const string_type& 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);
+ static S32 compareDict(const string_type& a, const string_type& b);
// Case *in*sensitive comparison with good handling of numbers. Does not use current locale.
// a.k.a. strdictcmp()
- static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b);
+ static S32 compareDictInsensitive(const string_type& a, const string_type& 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 );
+ static BOOL precedesDict( const string_type& a, const string_type& b );
// A replacement for strncpy.
// If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
@@ -340,7 +409,7 @@ public:
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);
+ static void copyInto(string_type& dst, const string_type& src, size_type offset);
static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
@@ -350,10 +419,10 @@ public:
#endif
private:
- LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
+ LL_COMMON_API static size_type getSubstitution(const string_type& instr, size_type& start, std::vector<string_type >& tokens);
};
-template<class T> std::basic_string<T> LLStringUtilBase<T>::null;
+template<class T> const std::basic_string<T> LLStringUtilBase<T>::null;
template<class T> std::string LLStringUtilBase<T>::sLocale;
typedef LLStringUtilBase<char> LLStringUtil;
@@ -404,6 +473,7 @@ inline std::string chop_tail_copy(
* @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.
*/
+LL_COMMON_API bool is_char_hex(char hex);
LL_COMMON_API U8 hex_as_nybble(char hex);
/**
@@ -423,7 +493,7 @@ LL_COMMON_API bool iswindividual(llwchar elem);
*/
// Make the incoming string a utf8 string. Replaces any unknown glyph
-// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
+// with the UNKNOWN_CHARACTER. Once any unknown glyph is found, the rest
// of the data may not be recovered.
LL_COMMON_API std::string rawstr_to_utf8(const std::string& raw);
@@ -451,10 +521,13 @@ LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str);
-
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str);
+#if LL_WINDOWS
+inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(utf16str);}
+#endif
+
// Length of this UTF32 string in bytes when transformed to UTF8
LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
@@ -546,7 +619,20 @@ using snprintf_hack::snprintf;
*
* This replaces the unsafe W2A macro from ATL.
*/
-LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in);
+LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in, unsigned int code_page);
+
+/**
+ * Converts a string to wide string.
+ *
+ * It will allocate memory for result string with "new []". Don't forget to release it with "delete []".
+ */
+LL_COMMON_API wchar_t* ll_convert_string_to_wide(const std::string& in, unsigned int code_page);
+
+/**
+ * Converts incoming string into urf8 string
+ *
+ */
+LL_COMMON_API std::string ll_convert_string_to_utf8_string(const std::string& in);
//@}
#endif // LL_WINDOWS
@@ -611,10 +697,325 @@ namespace LLStringFn
////////////////////////////////////////////////////////////
// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
// There is no LLWStringUtil::format implementation currently.
-// Calling thse for anything other than LLStringUtil will produce link errors.
+// Calling these for anything other than LLStringUtil will produce link errors.
////////////////////////////////////////////////////////////
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr, const string_type& delims)
+{
+ std::vector<string_type> tokens;
+ getTokens(instr, tokens, delims);
+ return tokens;
+}
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes)
+{
+ std::vector<string_type> tokens;
+ getTokens(instr, tokens, drop_delims, keep_delims, quotes);
+ return tokens;
+}
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes,
+ const string_type& escapes)
+{
+ std::vector<string_type> tokens;
+ getTokens(instr, tokens, drop_delims, keep_delims, quotes, escapes);
+ return tokens;
+}
+
+namespace LLStringUtilBaseImpl
+{
+
+/**
+ * Input string scanner helper for getTokens(), or really any other
+ * character-parsing routine that may have to deal with escape characters.
+ * This implementation defines the concept (also an interface, should you
+ * choose to implement the concept by subclassing) and provides trivial
+ * implementations for a string @em without escape processing.
+ */
+template <class T>
+struct InString
+{
+ typedef std::basic_string<T> string_type;
+ typedef typename string_type::const_iterator const_iterator;
+
+ InString(const_iterator b, const_iterator e):
+ mIter(b),
+ mEnd(e)
+ {}
+ virtual ~InString() {}
+
+ bool done() const { return mIter == mEnd; }
+ /// Is the current character (*mIter) escaped? This implementation can
+ /// answer trivially because it doesn't support escapes.
+ virtual bool escaped() const { return false; }
+ /// Obtain the current character and advance @c mIter.
+ virtual T next() { return *mIter++; }
+ /// Does the current character match specified character?
+ virtual bool is(T ch) const { return (! done()) && *mIter == ch; }
+ /// Is the current character any one of the specified characters?
+ virtual bool oneof(const string_type& delims) const
+ {
+ return (! done()) && LLStringUtilBase<T>::contains(delims, *mIter);
+ }
+
+ /**
+ * Scan forward from @from until either @a delim or end. This is primarily
+ * useful for processing quoted substrings.
+ *
+ * If we do see @a delim, append everything from @from until (excluding)
+ * @a delim to @a into, advance @c mIter to skip @a delim, and return @c
+ * true.
+ *
+ * If we do not see @a delim, do not alter @a into or @c mIter and return
+ * @c false. Do not pass GO, do not collect $200.
+ *
+ * @note The @c false case described above implements normal getTokens()
+ * treatment of an unmatched open quote: treat the quote character as if
+ * escaped, that is, simply collect it as part of the current token. Other
+ * plausible behaviors directly affect the way getTokens() deals with an
+ * unmatched quote: e.g. throwing an exception to treat it as an error, or
+ * assuming a close quote beyond end of string (in which case return @c
+ * true).
+ */
+ virtual bool collect_until(string_type& into, const_iterator from, T delim)
+ {
+ const_iterator found = std::find(from, mEnd, delim);
+ // If we didn't find delim, change nothing, just tell caller.
+ if (found == mEnd)
+ return false;
+ // Found delim! Append everything between from and found.
+ into.append(from, found);
+ // advance past delim in input
+ mIter = found + 1;
+ return true;
+ }
+
+ const_iterator mIter, mEnd;
+};
+
+/// InString subclass that handles escape characters
+template <class T>
+class InEscString: public InString<T>
+{
+public:
+ typedef InString<T> super;
+ typedef typename super::string_type string_type;
+ typedef typename super::const_iterator const_iterator;
+ using super::done;
+ using super::mIter;
+ using super::mEnd;
+
+ InEscString(const_iterator b, const_iterator e, const string_type& escapes):
+ super(b, e),
+ mEscapes(escapes)
+ {
+ // Even though we've already initialized 'mIter' via our base-class
+ // constructor, set it again to check for initial escape char.
+ setiter(b);
+ }
+
+ /// This implementation uses the answer cached by setiter().
+ virtual bool escaped() const { return mIsEsc; }
+ virtual T next()
+ {
+ // If we're looking at the escape character of an escape sequence,
+ // skip that character. This is the one time we can modify 'mIter'
+ // without using setiter: for this one case we DO NOT CARE if the
+ // escaped character is itself an escape.
+ if (mIsEsc)
+ ++mIter;
+ // If we were looking at an escape character, this is the escaped
+ // character; otherwise it's just the next character.
+ T result(*mIter);
+ // Advance mIter, checking for escape sequence.
+ setiter(mIter + 1);
+ return result;
+ }
+
+ virtual bool is(T ch) const
+ {
+ // Like base-class is(), except that an escaped character matches
+ // nothing.
+ return (! done()) && (! mIsEsc) && *mIter == ch;
+ }
+
+ virtual bool oneof(const string_type& delims) const
+ {
+ // Like base-class oneof(), except that an escaped character matches
+ // nothing.
+ return (! done()) && (! mIsEsc) && LLStringUtilBase<T>::contains(delims, *mIter);
+ }
+
+ virtual bool collect_until(string_type& into, const_iterator from, T delim)
+ {
+ // Deal with escapes in the characters we collect; that is, an escaped
+ // character must become just that character without the preceding
+ // escape. Collect characters in a separate string rather than
+ // directly appending to 'into' in case we do not find delim, in which
+ // case we're supposed to leave 'into' unmodified.
+ string_type collected;
+ // For scanning purposes, we're going to work directly with 'mIter'.
+ // Save its current value in case we fail to see delim.
+ const_iterator save_iter(mIter);
+ // Okay, set 'mIter', checking for escape.
+ setiter(from);
+ while (! done())
+ {
+ // If we see an unescaped delim, stop and report success.
+ if ((! mIsEsc) && *mIter == delim)
+ {
+ // Append collected chars to 'into'.
+ into.append(collected);
+ // Don't forget to advance 'mIter' past delim.
+ setiter(mIter + 1);
+ return true;
+ }
+ // We're not at end, and either we're not looking at delim or it's
+ // escaped. Collect this character and keep going.
+ collected.push_back(next());
+ }
+ // Here we hit 'mEnd' without ever seeing delim. Restore mIter and tell
+ // caller.
+ setiter(save_iter);
+ return false;
+ }
+
+private:
+ void setiter(const_iterator i)
+ {
+ mIter = i;
+
+ // Every time we change 'mIter', set 'mIsEsc' to be able to repetitively
+ // answer escaped() without having to rescan 'mEscapes'. mIsEsc caches
+ // contains(mEscapes, *mIter).
+
+ // We're looking at an escaped char if we're not already at end (that
+ // is, *mIter is even meaningful); if *mIter is in fact one of the
+ // specified escape characters; and if there's one more character
+ // following it. That is, if an escape character is the very last
+ // character of the input string, it loses its special meaning.
+ mIsEsc = (! done()) &&
+ LLStringUtilBase<T>::contains(mEscapes, *mIter) &&
+ (mIter+1) != mEnd;
+ }
+
+ const string_type mEscapes;
+ bool mIsEsc;
+};
+
+/// getTokens() implementation based on InString concept
+template <typename INSTRING, typename string_type>
+void getTokens(INSTRING& instr, std::vector<string_type>& tokens,
+ const string_type& drop_delims, const string_type& keep_delims,
+ const string_type& quotes)
+{
+ // There are times when we want to match either drop_delims or
+ // keep_delims. Concatenate them up front to speed things up.
+ string_type all_delims(drop_delims + keep_delims);
+ // no tokens yet
+ tokens.clear();
+
+ // try for another token
+ while (! instr.done())
+ {
+ // scan past any drop_delims
+ while (instr.oneof(drop_delims))
+ {
+ // skip this drop_delim
+ instr.next();
+ // but if that was the end of the string, done
+ if (instr.done())
+ return;
+ }
+ // found the start of another token: make a slot for it
+ tokens.push_back(string_type());
+ if (instr.oneof(keep_delims))
+ {
+ // *iter is a keep_delim, a token of exactly 1 character. Append
+ // that character to the new token and proceed.
+ tokens.back().push_back(instr.next());
+ continue;
+ }
+ // Here we have a non-delimiter token, which might consist of a mix of
+ // quoted and unquoted parts. Use bash rules for quoting: you can
+ // embed a quoted substring in the midst of an unquoted token (e.g.
+ // ~/"sub dir"/myfile.txt); you can ram two quoted substrings together
+ // to make a single token (e.g. 'He said, "'"Don't."'"'). We diverge
+ // from bash in that bash considers an unmatched quote an error. Our
+ // param signature doesn't allow for errors, so just pretend it's not
+ // a quote and embed it.
+ // At this level, keep scanning until we hit the next delimiter of
+ // either type (drop_delims or keep_delims).
+ while (! instr.oneof(all_delims))
+ {
+ // If we're looking at an open quote, search forward for
+ // a close quote, collecting characters along the way.
+ if (instr.oneof(quotes) &&
+ instr.collect_until(tokens.back(), instr.mIter+1, *instr.mIter))
+ {
+ // collect_until is cleverly designed to do exactly what we
+ // need here. No further action needed if it returns true.
+ }
+ else
+ {
+ // Either *iter isn't a quote, or there's no matching close
+ // quote: in other words, just an ordinary char. Append it to
+ // current token.
+ tokens.back().push_back(instr.next());
+ }
+ // having scanned that segment of this token, if we've reached the
+ // end of the string, we're done
+ if (instr.done())
+ return;
+ }
+ }
+}
+
+} // namespace LLStringUtilBaseImpl
+
+// static
+template <class T>
+void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
+ const string_type& drop_delims, const string_type& keep_delims,
+ const string_type& quotes)
+{
+ // Because this overload doesn't support escapes, use simple InString to
+ // manage input range.
+ LLStringUtilBaseImpl::InString<T> instring(string.begin(), string.end());
+ LLStringUtilBaseImpl::getTokens(instring, tokens, drop_delims, keep_delims, quotes);
+}
+
+// static
+template <class T>
+void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
+ const string_type& drop_delims, const string_type& keep_delims,
+ const string_type& quotes, const string_type& escapes)
+{
+ // This overload must deal with escapes. Delegate that to InEscString
+ // (unless there ARE no escapes).
+ boost::scoped_ptr< LLStringUtilBaseImpl::InString<T> > instrp;
+ if (escapes.empty())
+ instrp.reset(new LLStringUtilBaseImpl::InString<T>(string.begin(), string.end()));
+ else
+ instrp.reset(new LLStringUtilBaseImpl::InEscString<T>(string.begin(), string.end(), escapes));
+ LLStringUtilBaseImpl::getTokens(*instrp, tokens, drop_delims, keep_delims, quotes);
+}
// static
template<class T>
@@ -644,7 +1045,7 @@ S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
//static
template<class T>
-S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+S32 LLStringUtilBase<T>::compareStrings(const string_type& lhs, const string_type& rhs)
{
return LLStringOps::collate(lhs.c_str(), rhs.c_str());
}
@@ -670,8 +1071,8 @@ S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
}
else
{
- std::basic_string<T> lhs_string(lhs);
- std::basic_string<T> rhs_string(rhs);
+ string_type lhs_string(lhs);
+ string_type rhs_string(rhs);
LLStringUtilBase<T>::toUpper(lhs_string);
LLStringUtilBase<T>::toUpper(rhs_string);
result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
@@ -681,10 +1082,10 @@ S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
//static
template<class T>
-S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+S32 LLStringUtilBase<T>::compareInsensitive(const string_type& lhs, const string_type& rhs)
{
- std::basic_string<T> lhs_string(lhs);
- std::basic_string<T> rhs_string(rhs);
+ string_type lhs_string(lhs);
+ string_type rhs_string(rhs);
LLStringUtilBase<T>::toUpper(lhs_string);
LLStringUtilBase<T>::toUpper(rhs_string);
return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
@@ -695,7 +1096,7 @@ S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, con
//static
template<class T>
-S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+S32 LLStringUtilBase<T>::compareDict(const string_type& astr, const string_type& bstr)
{
const T* a = astr.c_str();
const T* b = bstr.c_str();
@@ -736,7 +1137,7 @@ S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std
// static
template<class T>
-S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+S32 LLStringUtilBase<T>::compareDictInsensitive(const string_type& astr, const string_type& bstr)
{
const T* a = astr.c_str();
const T* b = bstr.c_str();
@@ -771,7 +1172,7 @@ S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr
// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
// static
template<class T>
-BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+BOOL LLStringUtilBase<T>::precedesDict( const string_type& a, const string_type& b )
{
if( a.size() && b.size() )
{
@@ -785,7 +1186,7 @@ BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std
//static
template<class T>
-void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
+void LLStringUtilBase<T>::toUpper(string_type& string)
{
if( !string.empty() )
{
@@ -799,7 +1200,7 @@ void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
//static
template<class T>
-void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
+void LLStringUtilBase<T>::toLower(string_type& string)
{
if( !string.empty() )
{
@@ -813,7 +1214,7 @@ void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
//static
template<class T>
-void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
+void LLStringUtilBase<T>::trimHead(string_type& string)
{
if( !string.empty() )
{
@@ -828,7 +1229,7 @@ void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
//static
template<class T>
-void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
+void LLStringUtilBase<T>::trimTail(string_type& string)
{
if( string.size() )
{
@@ -847,7 +1248,7 @@ void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
// Replace line feeds with carriage return-line feed pairs.
//static
template<class T>
-void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
+void LLStringUtilBase<T>::addCRLF(string_type& string)
{
const T LF = 10;
const T CR = 13;
@@ -889,7 +1290,7 @@ void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
// Remove all carriage returns
//static
template<class T>
-void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
+void LLStringUtilBase<T>::removeCRLF(string_type& string)
{
const T CR = 13;
@@ -910,10 +1311,10 @@ void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
//static
template<class T>
-void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+void LLStringUtilBase<T>::replaceChar( string_type& string, T target, T replacement )
{
size_type found_pos = 0;
- while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+ while( (found_pos = string.find(target, found_pos)) != string_type::npos )
{
string[found_pos] = replacement;
found_pos++; // avoid infinite defeat if target == replacement
@@ -922,10 +1323,10 @@ void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T
//static
template<class T>
-void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement )
+void LLStringUtilBase<T>::replaceString( string_type& string, string_type target, string_type replacement )
{
size_type found_pos = 0;
- while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+ while( (found_pos = string.find(target, found_pos)) != string_type::npos )
{
string.replace( found_pos, target.length(), replacement );
found_pos += replacement.length(); // avoid infinite defeat if replacement contains target
@@ -934,7 +1335,7 @@ void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basi
//static
template<class T>
-void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+void LLStringUtilBase<T>::replaceNonstandardASCII( string_type& string, T replacement )
{
const char LF = 10;
const S8 MIN = 32;
@@ -954,12 +1355,12 @@ void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string,
//static
template<class T>
-void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
+void LLStringUtilBase<T>::replaceTabsWithSpaces( string_type& str, size_type spaces_per_tab )
{
const T TAB = '\t';
const T SPACE = ' ';
- std::basic_string<T> out_str;
+ string_type out_str;
// Replace tabs with spaces
for (size_type i = 0; i < str.length(); i++)
{
@@ -978,7 +1379,7 @@ void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size
//static
template<class T>
-BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string)
+BOOL LLStringUtilBase<T>::containsNonprintable(const string_type& string)
{
const char MIN = 32;
BOOL rv = FALSE;
@@ -995,7 +1396,7 @@ BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& strin
//static
template<class T>
-void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
+void LLStringUtilBase<T>::stripNonprintable(string_type& string)
{
const char MIN = 32;
size_type j = 0;
@@ -1026,8 +1427,43 @@ void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
delete []c_string;
}
+template<class T>
+std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
+ const string_type& triggers,
+ const string_type& escape)
+{
+ size_type len(str.length());
+ // If the string is already quoted, assume user knows what s/he's doing.
+ if (len >= 2 && str[0] == '"' && str[len-1] == '"')
+ {
+ return str;
+ }
+
+ // Not already quoted: do we need to? triggers.empty() is a special case
+ // meaning "always quote."
+ if ((! triggers.empty()) && str.find_first_of(triggers) == string_type::npos)
+ {
+ // no trigger characters, don't bother quoting
+ return str;
+ }
+
+ // For whatever reason, we must quote this string.
+ string_type result;
+ result.push_back('"');
+ for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
+ {
+ if (*ci == '"')
+ {
+ result.append(escape);
+ }
+ result.push_back(*ci);
+ }
+ result.push_back('"');
+ return result;
+}
+
template<class T>
-void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string)
+void LLStringUtilBase<T>::_makeASCII(string_type& string)
{
// Replace non-ASCII chars with LL_UNKNOWN_CHAR
for (size_type i = 0; i < string.length(); i++)
@@ -1057,7 +1493,7 @@ void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )
// static
template<class T>
-void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+void LLStringUtilBase<T>::copyInto(string_type& dst, const string_type& src, size_type offset)
{
if ( offset == dst.length() )
{
@@ -1067,7 +1503,7 @@ void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_s
}
else
{
- std::basic_string<T> tail = dst.substr(offset);
+ string_type tail = dst.substr(offset);
dst = dst.substr(0, offset);
dst += src;
@@ -1078,7 +1514,7 @@ void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_s
// True if this is the head of s.
//static
template<class T>
-BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s )
+BOOL LLStringUtilBase<T>::isHead( const string_type& string, const T* s )
{
if( string.empty() )
{
@@ -1094,8 +1530,8 @@ BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s
// static
template<class T>
bool LLStringUtilBase<T>::startsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr)
+ const string_type& string,
+ const string_type& substr)
{
if(string.empty() || (substr.empty())) return false;
if(0 == string.find(substr)) return true;
@@ -1105,8 +1541,8 @@ bool LLStringUtilBase<T>::startsWith(
// static
template<class T>
bool LLStringUtilBase<T>::endsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr)
+ const string_type& string,
+ const string_type& substr)
{
if(string.empty() || (substr.empty())) return false;
std::string::size_type idx = string.rfind(substr);
@@ -1116,14 +1552,14 @@ bool LLStringUtilBase<T>::endsWith(
template<class T>
-BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+BOOL LLStringUtilBase<T>::convertToBOOL(const string_type& string, BOOL& value)
{
if( string.empty() )
{
return FALSE;
}
- std::basic_string<T> temp( string );
+ string_type temp( string );
trim(temp);
if(
(temp == "1") ||
@@ -1153,7 +1589,7 @@ BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
+BOOL LLStringUtilBase<T>::convertToU8(const string_type& string, U8& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@@ -1166,7 +1602,7 @@ BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& va
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
+BOOL LLStringUtilBase<T>::convertToS8(const string_type& string, S8& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@@ -1179,7 +1615,7 @@ BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& va
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
+BOOL LLStringUtilBase<T>::convertToS16(const string_type& string, S16& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@@ -1192,7 +1628,7 @@ BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16&
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
+BOOL LLStringUtilBase<T>::convertToU16(const string_type& string, U16& value)
{
S32 value32 = 0;
BOOL success = convertToS32(string, value32);
@@ -1205,17 +1641,17 @@ BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16&
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
+BOOL LLStringUtilBase<T>::convertToU32(const string_type& string, U32& value)
{
if( string.empty() )
{
return FALSE;
}
- std::basic_string<T> temp( string );
+ string_type temp( string );
trim(temp);
U32 v;
- std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ std::basic_istringstream<T> i_stream((string_type)temp);
if(i_stream >> v)
{
value = v;
@@ -1225,17 +1661,17 @@ BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32&
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
+BOOL LLStringUtilBase<T>::convertToS32(const string_type& string, S32& value)
{
if( string.empty() )
{
return FALSE;
}
- std::basic_string<T> temp( string );
+ string_type temp( string );
trim(temp);
S32 v;
- std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ std::basic_istringstream<T> i_stream((string_type)temp);
if(i_stream >> v)
{
//TODO: figure out overflow and underflow reporting here
@@ -1252,7 +1688,7 @@ BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32&
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
+BOOL LLStringUtilBase<T>::convertToF32(const string_type& string, F32& value)
{
F64 value64 = 0.0;
BOOL success = convertToF64(string, value64);
@@ -1265,17 +1701,17 @@ BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32&
}
template<class T>
-BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+BOOL LLStringUtilBase<T>::convertToF64(const string_type& string, F64& value)
{
if( string.empty() )
{
return FALSE;
}
- std::basic_string<T> temp( string );
+ string_type temp( string );
trim(temp);
F64 v;
- std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ std::basic_istringstream<T> i_stream((string_type)temp);
if(i_stream >> v)
{
//TODO: figure out overflow and underflow reporting here
@@ -1292,7 +1728,7 @@ BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64&
}
template<class T>
-void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count)
+void LLStringUtilBase<T>::truncate(string_type& string, size_type count)
{
size_type cur_size = string.size();
string.resize(count < cur_size ? count : cur_size);
diff --git a/indra/llcommon/llstringtable.cpp b/indra/llcommon/llstringtable.cpp
index 2f3a994d8f..f288999964 100644..100755
--- a/indra/llcommon/llstringtable.cpp
+++ b/indra/llcommon/llstringtable.cpp
@@ -3,31 +3,25 @@
* @brief The LLStringTable class provides a _fast_ method for finding
* unique copies of strings.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -323,7 +317,7 @@ void LLStringTable::removeString(const char *str)
mUniqueEntries--;
if (mUniqueEntries < 0)
{
- llerror("LLStringTable:removeString trying to remove too many strings!", 0);
+ LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL;
}
delete iter->second;
mStringHash.erase(iter);
@@ -349,7 +343,7 @@ void LLStringTable::removeString(const char *str)
mUniqueEntries--;
if (mUniqueEntries < 0)
{
- llerror("LLStringTable:removeString trying to remove too many strings!", 0);
+ LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL;
}
strlist->remove(entry);
delete entry;
diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h
index d40c9d8dfd..ff09e71677 100644..100755
--- a/indra/llcommon/llstringtable.h
+++ b/indra/llcommon/llstringtable.h
@@ -3,31 +3,25 @@
* @brief The LLStringTable class provides a _fast_ method for finding
* unique copies of strings.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -48,14 +42,6 @@
//# define STRING_TABLE_HASH_MAP 1
#endif
-#if STRING_TABLE_HASH_MAP
-# if LL_WINDOWS
-# include <hash_map>
-# else
-# include <ext/hash_map>
-# endif
-#endif
-
const U32 MAX_STRINGS_LENGTH = 256;
class LL_COMMON_API LLStringTableEntry
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 0272c55db2..1a66612e87 100644..100755
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -1,64 +1,91 @@
/**
* @file llsys.cpp
- * @brief Impelementation of the basic system query functions.
+ * @brief Implementation of the basic system query functions.
*
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
#include "linden_common.h"
#include "llsys.h"
#include <iostream>
-#ifdef LL_STANDALONE
+#ifdef LL_USESYSTEMLIBS
# include <zlib.h>
#else
# include "zlib/zlib.h"
#endif
#include "llprocessor.h"
+#include "llerrorcontrol.h"
+#include "llevents.h"
+#include "llformat.h"
+#include "lltimer.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include <boost/bind.hpp>
+#include <boost/circular_buffer.hpp>
+#include <boost/regex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/range.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/type_traits/is_integral.hpp>
+#include <boost/type_traits/is_float.hpp>
+
+using namespace llsd;
#if LL_WINDOWS
-# define WIN32_LEAN_AND_MEAN
-# include <winsock2.h>
-# include <windows.h>
+# include "llwin32headerslean.h"
+# include <psapi.h> // GetPerformanceInfo() et al.
#elif LL_DARWIN
# include <errno.h>
# include <sys/sysctl.h>
# include <sys/utsname.h>
# include <stdint.h>
+# include <CoreServices/CoreServices.h>
+# include <stdexcept>
+# include <mach/host_info.h>
+# include <mach/mach_host.h>
+# include <mach/task.h>
+# include <mach/task_info.h>
+
+// disable warnings about Gestalt calls being deprecated
+// until Apple get's on the ball and provides an alternative
+//
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+
#elif LL_LINUX
# include <errno.h>
# include <sys/utsname.h>
# include <unistd.h>
# include <sys/sysinfo.h>
+# include <stdexcept>
const char MEMINFO_FILE[] = "/proc/meminfo";
-const char CPUINFO_FILE[] = "/proc/cpuinfo";
+# include <gnu/libc-version.h>
#elif LL_SOLARIS
# include <stdio.h>
# include <unistd.h>
@@ -72,11 +99,21 @@ const char CPUINFO_FILE[] = "/proc/cpuinfo";
extern int errno;
#endif
-
-static const S32 CPUINFO_BUFFER_SIZE = 16383;
LLCPUInfo gSysCPU;
+// Don't log memory info any more often than this. It also serves as our
+// framerate sample size.
+static const F32 MEM_INFO_THROTTLE = 20;
+// Sliding window of samples. We intentionally limit the length of time we
+// remember "the slowest" framerate because framerate is very slow at login.
+// If we only triggered FrameWatcher logging when the session framerate
+// dropped below the login framerate, we'd have very little additional data.
+static const F32 MEM_INFO_WINDOW = 10*60;
+
#if LL_WINDOWS
+// We cannot trust GetVersionEx function on Win8.1 , we should check this value when creating OS string
+static const U32 WINNT_WINBLUE = 0x0603;
+
#ifndef DLLVERSIONINFO
typedef struct _DllVersionInfo
{
@@ -145,13 +182,67 @@ bool get_shell32_dll_version(DWORD& major, DWORD& minor, DWORD& build_number)
}
#endif // LL_WINDOWS
+// Wrap boost::regex_match() with a function that doesn't throw.
+template <typename S, typename M, typename R>
+static bool regex_match_no_exc(const S& string, M& match, const R& regex)
+{
+ try
+ {
+ return boost::regex_match(string, match, regex);
+ }
+ catch (const std::runtime_error& e)
+ {
+ LL_WARNS("LLMemoryInfo") << "error matching with '" << regex.str() << "': "
+ << e.what() << ":\n'" << string << "'" << LL_ENDL;
+ return false;
+ }
+}
+
+// Wrap boost::regex_search() with a function that doesn't throw.
+template <typename S, typename M, typename R>
+static bool regex_search_no_exc(const S& string, M& match, const R& regex)
+{
+ try
+ {
+ return boost::regex_search(string, match, regex);
+ }
+ catch (const std::runtime_error& e)
+ {
+ LL_WARNS("LLMemoryInfo") << "error searching with '" << regex.str() << "': "
+ << e.what() << ":\n'" << string << "'" << LL_ENDL;
+ return false;
+ }
+}
+
+#if LL_WINDOWS
+// GetVersionEx should not works correct with Windows 8.1 and the later version. We need to check this case
+static bool check_for_version(WORD wMajorVersion, WORD wMinorVersion, WORD wServicePackMajor)
+{
+ OSVERSIONINFOEXW osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
+ DWORDLONG const dwlConditionMask = VerSetConditionMask(
+ VerSetConditionMask(
+ VerSetConditionMask(
+ 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
+ VER_MINORVERSION, VER_GREATER_EQUAL),
+ VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+
+ osvi.dwMajorVersion = wMajorVersion;
+ osvi.dwMinorVersion = wMinorVersion;
+ osvi.wServicePackMajor = wServicePackMajor;
+
+ return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
+}
+#endif
+
+
LLOSInfo::LLOSInfo() :
- mMajorVer(0), mMinorVer(0), mBuild(0)
+ mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("")
{
#if LL_WINDOWS
OSVERSIONINFOEX osvi;
BOOL bOsVersionInfoEx;
+ BOOL bShouldUseShellVersion = false;
// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
@@ -194,22 +285,38 @@ LLOSInfo::LLOSInfo() :
if(osvi.wProductType == VER_NT_WORKSTATION)
mOSStringSimple = "Microsoft Windows XP x64 Edition ";
else
- mOSStringSimple = "Microsoft Windows Server 2003 ";
+ mOSStringSimple = "Microsoft Windows Server 2003 ";
}
- else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 1)
+ else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion <= 2)
{
if(osvi.dwMinorVersion == 0)
{
- mOSStringSimple = "Microsoft Windows Vista ";
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSStringSimple = "Microsoft Windows Vista ";
+ else
+ mOSStringSimple = "Windows Server 2008 ";
}
else if(osvi.dwMinorVersion == 1)
{
- mOSStringSimple = "Microsoft Windows 7 ";
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSStringSimple = "Microsoft Windows 7 ";
+ else
+ mOSStringSimple = "Windows Server 2008 R2 ";
}
-
- if(osvi.wProductType != VER_NT_WORKSTATION)
+ else if(osvi.dwMinorVersion == 2)
{
- mOSStringSimple += "Server ";
+ if (check_for_version(HIBYTE(WINNT_WINBLUE), LOBYTE(WINNT_WINBLUE), 0))
+ {
+ mOSStringSimple = "Microsoft Windows 8.1 ";
+ bShouldUseShellVersion = true; // GetVersionEx failed, going to use shell version
+ }
+ else
+ {
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSStringSimple = "Microsoft Windows 8 ";
+ else
+ mOSStringSimple = "Windows Server 2012 ";
+ }
}
///get native system info if available..
@@ -276,9 +383,8 @@ LLOSInfo::LLOSInfo() :
}
else
{
- tmpstr = llformat("%s (Build %d)",
- csdversion.c_str(),
- (osvi.dwBuildNumber & 0xffff));
+ tmpstr = !bShouldUseShellVersion ? llformat("%s (Build %d)", csdversion.c_str(), (osvi.dwBuildNumber & 0xffff)):
+ llformat("%s (Build %d)", csdversion.c_str(), shell32_build);
}
mOSString = mOSStringSimple + tmpstr;
@@ -314,8 +420,7 @@ LLOSInfo::LLOSInfo() :
std::string compatibility_mode;
if(got_shell32_version)
{
- if(osvi.dwMajorVersion != shell32_major
- || osvi.dwMinorVersion != shell32_minor)
+ if((osvi.dwMajorVersion != shell32_major || osvi.dwMinorVersion != shell32_minor) && !bShouldUseShellVersion)
{
compatibility_mode = llformat(" compatibility mode. real ver: %d.%d (Build %d)",
shell32_major,
@@ -325,7 +430,58 @@ LLOSInfo::LLOSInfo() :
}
mOSString += compatibility_mode;
-#else
+#elif LL_DARWIN
+
+ // Initialize mOSStringSimple to something like:
+ // "Mac OS X 10.6.7"
+ {
+ const char * DARWIN_PRODUCT_NAME = "Mac OS X";
+
+ SInt32 major_version, minor_version, bugfix_version;
+ OSErr r1 = Gestalt(gestaltSystemVersionMajor, &major_version);
+ OSErr r2 = Gestalt(gestaltSystemVersionMinor, &minor_version);
+ OSErr r3 = Gestalt(gestaltSystemVersionBugFix, &bugfix_version);
+
+ if((r1 == noErr) && (r2 == noErr) && (r3 == noErr))
+ {
+ mMajorVer = major_version;
+ mMinorVer = minor_version;
+ mBuild = bugfix_version;
+
+ std::stringstream os_version_string;
+ os_version_string << DARWIN_PRODUCT_NAME << " " << mMajorVer << "." << mMinorVer << "." << mBuild;
+
+ // Put it in the OS string we are compiling
+ mOSStringSimple.append(os_version_string.str());
+ }
+ else
+ {
+ mOSStringSimple.append("Unable to collect OS info");
+ }
+ }
+
+ // Initialize mOSString to something like:
+ // "Mac OS X 10.6.7 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386"
+ struct utsname un;
+ if(uname(&un) != -1)
+ {
+ mOSString = mOSStringSimple;
+ mOSString.append(" ");
+ mOSString.append(un.sysname);
+ mOSString.append(" ");
+ mOSString.append(un.release);
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+ }
+ else
+ {
+ mOSString = mOSStringSimple;
+ }
+
+#elif LL_LINUX
+
struct utsname un;
if(uname(&un) != -1)
{
@@ -341,15 +497,103 @@ LLOSInfo::LLOSInfo() :
// Simplify 'Simple'
std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0));
- if (ostype == "Darwin")
+ if (ostype == "Linux")
{
- // Only care about major Darwin versions, truncate at first '.'
- S32 idx1 = mOSStringSimple.find_first_of(".", 0);
- std::string simple = mOSStringSimple.substr(0, idx1);
+ // Only care about major and minor Linux versions, truncate at second '.'
+ std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0);
+ std::string::size_type idx2 = (idx1 != std::string::npos) ? mOSStringSimple.find_first_of(".", idx1+1) : std::string::npos;
+ std::string simple = mOSStringSimple.substr(0, idx2);
if (simple.length() > 0)
mOSStringSimple = simple;
}
- else if (ostype == "Linux")
+ }
+ else
+ {
+ mOSStringSimple.append("Unable to collect OS info");
+ mOSString = mOSStringSimple;
+ }
+
+ const char OS_VERSION_MATCH_EXPRESSION[] = "([0-9]+)\\.([0-9]+)(\\.([0-9]+))?";
+ boost::regex os_version_parse(OS_VERSION_MATCH_EXPRESSION);
+ boost::smatch matched;
+
+ std::string glibc_version(gnu_get_libc_version());
+ if ( regex_match_no_exc(glibc_version, matched, os_version_parse) )
+ {
+ LL_INFOS("AppInit") << "Using glibc version '" << glibc_version << "' as OS version" << LL_ENDL;
+
+ std::string version_value;
+
+ if ( matched[1].matched ) // Major version
+ {
+ version_value.assign(matched[1].first, matched[1].second);
+ if (sscanf(version_value.c_str(), "%d", &mMajorVer) != 1)
+ {
+ LL_WARNS("AppInit") << "failed to parse major version '" << version_value << "' as a number" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS("AppInit")
+ << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION
+ << "' returned true, but major version [1] did not match"
+ << LL_ENDL;
+ }
+
+ if ( matched[2].matched ) // Minor version
+ {
+ version_value.assign(matched[2].first, matched[2].second);
+ if (sscanf(version_value.c_str(), "%d", &mMinorVer) != 1)
+ {
+ LL_ERRS("AppInit") << "failed to parse minor version '" << version_value << "' as a number" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS("AppInit")
+ << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION
+ << "' returned true, but minor version [1] did not match"
+ << LL_ENDL;
+ }
+
+ if ( matched[4].matched ) // Build version (optional) - note that [3] includes the '.'
+ {
+ version_value.assign(matched[4].first, matched[4].second);
+ if (sscanf(version_value.c_str(), "%d", &mBuild) != 1)
+ {
+ LL_ERRS("AppInit") << "failed to parse build version '" << version_value << "' as a number" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_INFOS("AppInit")
+ << "OS build version not provided; using zero"
+ << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS("AppInit") << "glibc version '" << glibc_version << "' cannot be parsed to three numbers; using all zeros" << LL_ENDL;
+ }
+
+#else
+
+ struct utsname un;
+ if(uname(&un) != -1)
+ {
+ mOSStringSimple.append(un.sysname);
+ mOSStringSimple.append(" ");
+ mOSStringSimple.append(un.release);
+
+ mOSString = mOSStringSimple;
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+
+ // Simplify 'Simple'
+ std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0));
+ if (ostype == "Linux")
{
// Only care about major and minor Linux versions, truncate at second '.'
std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0);
@@ -364,8 +608,13 @@ LLOSInfo::LLOSInfo() :
mOSStringSimple.append("Unable to collect OS info");
mOSString = mOSStringSimple;
}
+
#endif
+ std::stringstream dotted_version_string;
+ dotted_version_string << mMajorVer << "." << mMinorVer << "." << mBuild;
+ mOSVersionString.append(dotted_version_string.str());
+
}
#ifndef LL_WINDOWS
@@ -393,7 +642,7 @@ S32 LLOSInfo::getMaxOpenFiles()
}
else
{
- llerrs << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << llendl;
+ LL_ERRS() << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << LL_ENDL;
}
}
}
@@ -416,7 +665,10 @@ const std::string& LLOSInfo::getOSStringSimple() const
return mOSStringSimple;
}
-const S32 STATUS_SIZE = 8192;
+const std::string& LLOSInfo::getOSVersionString() const
+{
+ return mOSVersionString;
+}
//static
U32 LLOSInfo::getProcessVirtualSizeKB()
@@ -425,6 +677,7 @@ U32 LLOSInfo::getProcessVirtualSizeKB()
#if LL_WINDOWS
#endif
#if LL_LINUX
+# define STATUS_SIZE 2048
LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb");
if (status_filep)
{
@@ -447,12 +700,12 @@ U32 LLOSInfo::getProcessVirtualSizeKB()
sprintf(proc_ps, "/proc/%d/psinfo", (int)getpid());
int proc_fd = -1;
if((proc_fd = open(proc_ps, O_RDONLY)) == -1){
- llwarns << "unable to open " << proc_ps << llendl;
+ LL_WARNS() << "unable to open " << proc_ps << LL_ENDL;
return 0;
}
psinfo_t proc_psinfo;
if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
- llwarns << "Unable to read " << proc_ps << llendl;
+ LL_WARNS() << "Unable to read " << proc_ps << LL_ENDL;
close(proc_fd);
return 0;
}
@@ -493,12 +746,12 @@ U32 LLOSInfo::getProcessResidentSizeKB()
sprintf(proc_ps, "/proc/%d/psinfo", (int)getpid());
int proc_fd = -1;
if((proc_fd = open(proc_ps, O_RDONLY)) == -1){
- llwarns << "unable to open " << proc_ps << llendl;
+ LL_WARNS() << "unable to open " << proc_ps << LL_ENDL;
return 0;
}
psinfo_t proc_psinfo;
if(read(proc_fd, &proc_psinfo, sizeof(psinfo_t)) != sizeof(psinfo_t)){
- llwarns << "Unable to read " << proc_ps << llendl;
+ LL_WARNS() << "Unable to read " << proc_ps << LL_ENDL;
close(proc_fd);
return 0;
}
@@ -513,71 +766,22 @@ U32 LLOSInfo::getProcessResidentSizeKB()
LLCPUInfo::LLCPUInfo()
{
std::ostringstream out;
- CProcessor proc;
- const ProcessorInfo* info = proc.GetCPUInfo();
+ LLProcessorInfo proc;
// proc.WriteInfoTextFile("procInfo.txt");
- mHasSSE = info->_Ext.SSE_StreamingSIMD_Extensions;
- mHasSSE2 = info->_Ext.SSE2_StreamingSIMD2_Extensions;
- mHasAltivec = info->_Ext.Altivec_Extensions;
- mCPUMhz = (S32)(proc.GetCPUFrequency(50)/1000000.0);
- mFamily.assign( info->strFamily );
+ mHasSSE = proc.hasSSE();
+ mHasSSE2 = proc.hasSSE2();
+ mHasAltivec = proc.hasAltivec();
+ mCPUMHz = (F64)proc.getCPUFrequency();
+ mFamily = proc.getCPUFamilyName();
mCPUString = "Unknown";
-#if LL_WINDOWS || LL_DARWIN || LL_SOLARIS
- out << proc.strCPUName;
- if (200 < mCPUMhz && mCPUMhz < 10000) // *NOTE: cpu speed is often way wrong, do a sanity check
+ out << proc.getCPUBrandName();
+ if (200 < mCPUMHz && mCPUMHz < 10000) // *NOTE: cpu speed is often way wrong, do a sanity check
{
- out << " (" << mCPUMhz << " MHz)";
+ out << " (" << mCPUMHz << " MHz)";
}
mCPUString = out.str();
-
-#elif LL_LINUX
- std::map< std::string, std::string > cpuinfo;
- LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb");
- if(cpuinfo_fp)
- {
- char line[MAX_STRING];
- memset(line, 0, MAX_STRING);
- while(fgets(line, MAX_STRING, cpuinfo_fp))
- {
- // /proc/cpuinfo on Linux looks like:
- // name\t*: value\n
- char* tabspot = strchr( line, '\t' );
- if (tabspot == NULL)
- continue;
- char* colspot = strchr( tabspot, ':' );
- if (colspot == NULL)
- continue;
- char* spacespot = strchr( colspot, ' ' );
- if (spacespot == NULL)
- continue;
- char* nlspot = strchr( line, '\n' );
- if (nlspot == NULL)
- nlspot = line + strlen( line ); // Fallback to terminating NUL
- std::string linename( line, tabspot );
- std::string llinename(linename);
- LLStringUtil::toLower(llinename);
- std::string lineval( spacespot + 1, nlspot );
- cpuinfo[ llinename ] = lineval;
- }
- fclose(cpuinfo_fp);
- }
-# if LL_X86
- std::string flags = " " + cpuinfo["flags"] + " ";
- LLStringUtil::toLower(flags);
- mHasSSE = ( flags.find( " sse " ) != std::string::npos );
- mHasSSE2 = ( flags.find( " sse2 " ) != std::string::npos );
-
- F64 mhz;
- if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz)
- && 200.0 < mhz && mhz < 10000.0)
- {
- mCPUMhz = (S32)llrint(mhz);
- }
- if (!cpuinfo["model name"].empty())
- mCPUString = cpuinfo["model name"];
-# endif // LL_X86
-#endif // LL_LINUX
+ LLStringUtil::trim(mCPUString);
}
bool LLCPUInfo::hasAltivec() const
@@ -595,9 +799,9 @@ bool LLCPUInfo::hasSSE2() const
return mHasSSE2;
}
-S32 LLCPUInfo::getMhz() const
+F64 LLCPUInfo::getMHz() const
{
- return mCPUMhz;
+ return mCPUMHz;
}
std::string LLCPUInfo::getCPUString() const
@@ -607,53 +811,62 @@ std::string LLCPUInfo::getCPUString() const
void LLCPUInfo::stream(std::ostream& s) const
{
-#if LL_WINDOWS || LL_DARWIN || LL_SOLARIS
// 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 information" << std::endl;
- }
-#else
- // *NOTE: This works on linux. What will it do on other systems?
- LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb");
- if(cpuinfo)
- {
- char line[MAX_STRING];
- memset(line, 0, MAX_STRING);
- while(fgets(line, MAX_STRING, cpuinfo))
- {
- line[strlen(line)-1] = ' ';
- s << line;
- }
- fclose(cpuinfo);
- s << std::endl;
- }
- else
- {
- s << "Unable to collect processor information" << std::endl;
- }
-#endif
+ s << LLProcessorInfo().getCPUFeatureDescription();
+
// These are interesting as they reflect our internal view of the
// CPU's attributes regardless of platform
s << "->mHasSSE: " << (U32)mHasSSE << std::endl;
s << "->mHasSSE2: " << (U32)mHasSSE2 << std::endl;
s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl;
- s << "->mCPUMhz: " << mCPUMhz << std::endl;
+ s << "->mCPUMHz: " << mCPUMHz << std::endl;
s << "->mCPUString: " << mCPUString << std::endl;
}
+// Helper class for LLMemoryInfo: accumulate stats in the form we store for
+// LLMemoryInfo::getStatsMap().
+class Stats
+{
+public:
+ Stats():
+ mStats(LLSD::emptyMap())
+ {}
+
+ // Store every integer type as LLSD::Integer.
+ template <class T>
+ void add(const LLSD::String& name, const T& value,
+ typename boost::enable_if<boost::is_integral<T> >::type* = 0)
+ {
+ mStats[name] = LLSD::Integer(value);
+ }
+
+ // Store every floating-point type as LLSD::Real.
+ template <class T>
+ void add(const LLSD::String& name, const T& value,
+ typename boost::enable_if<boost::is_float<T> >::type* = 0)
+ {
+ mStats[name] = LLSD::Real(value);
+ }
+
+ // Hope that LLSD::Date values are sufficiently unambiguous.
+ void add(const LLSD::String& name, const LLSD::Date& value)
+ {
+ mStats[name] = value;
+ }
+
+ LLSD get() const { return mStats; }
+
+private:
+ LLSD mStats;
+};
+
LLMemoryInfo::LLMemoryInfo()
{
+ refresh();
}
#if LL_WINDOWS
-static U32 LLMemoryAdjustKBResult(U32 inKB)
+static U32Kilobytes LLMemoryAdjustKBResult(U32Kilobytes inKB)
{
// Moved this here from llfloaterabout.cpp
@@ -664,20 +877,16 @@ static U32 LLMemoryAdjustKBResult(U32 inKB)
// returned from the GetMemoryStatusEx function. Here we keep the
// original adjustment from llfoaterabout.cpp until this can be
// fixed somehow.
- inKB += 1024;
+ inKB += U32Megabytes(1);
return inKB;
}
#endif
-U32 LLMemoryInfo::getPhysicalMemoryKB() const
+U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
{
#if LL_WINDOWS
- MEMORYSTATUSEX state;
- state.dwLength = sizeof(state);
- GlobalMemoryStatusEx(&state);
-
- return LLMemoryAdjustKBResult((U32)(state.ullTotalPhys >> 10));
+ return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger()));
#elif LL_DARWIN
// This might work on Linux as well. Someone check...
@@ -687,17 +896,17 @@ U32 LLMemoryInfo::getPhysicalMemoryKB() const
size_t len = sizeof(phys);
sysctl(mib, 2, &phys, &len, NULL, 0);
- return (U32)(phys >> 10);
+ return U64Bytes(phys);
#elif LL_LINUX
U64 phys = 0;
phys = (U64)(getpagesize()) * (U64)(get_phys_pages());
- return (U32)(phys >> 10);
+ return U64Bytes(phys);
#elif LL_SOLARIS
U64 phys = 0;
phys = (U64)(getpagesize()) * (U64)(sysconf(_SC_PHYS_PAGES));
- return (U32)(phys >> 10);
+ return U64Bytes(phys);
#else
return 0;
@@ -705,74 +914,393 @@ U32 LLMemoryInfo::getPhysicalMemoryKB() const
#endif
}
-U32 LLMemoryInfo::getPhysicalMemoryClamped() const
+U32Bytes LLMemoryInfo::getPhysicalMemoryClamped() const
{
// Return the total physical memory in bytes, but clamp it
// to no more than U32_MAX
- U32 phys_kb = getPhysicalMemoryKB();
- if (phys_kb >= 4194304 /* 4GB in KB */)
+ U32Kilobytes phys_kb = getPhysicalMemoryKB();
+ if (phys_kb >= U32Gigabytes(4))
{
- return U32_MAX;
+ return U32Bytes(U32_MAX);
}
else
{
- return phys_kb << 10;
+ return phys_kb;
}
}
+//static
+void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb)
+{
+#if LL_WINDOWS
+ // Sigh, this shouldn't be a static method, then we wouldn't have to
+ // reload this data separately from refresh()
+ LLSD statsMap(loadStatsMap());
+
+ avail_physical_mem_kb = (U32Kilobytes)statsMap["Avail Physical KB"].asInteger();
+ avail_virtual_mem_kb = (U32Kilobytes)statsMap["Avail Virtual KB"].asInteger();
+
+#elif LL_DARWIN
+ // mStatsMap is derived from vm_stat, look for (e.g.) "kb free":
+ // $ vm_stat
+ // Mach Virtual Memory Statistics: (page size of 4096 bytes)
+ // Pages free: 462078.
+ // Pages active: 142010.
+ // Pages inactive: 220007.
+ // Pages wired down: 159552.
+ // "Translation faults": 220825184.
+ // Pages copy-on-write: 2104153.
+ // Pages zero filled: 167034876.
+ // Pages reactivated: 65153.
+ // Pageins: 2097212.
+ // Pageouts: 41759.
+ // Object cache: 841598 hits of 7629869 lookups (11% hit rate)
+ avail_physical_mem_kb = (U32Kilobytes)-1 ;
+ avail_virtual_mem_kb = (U32Kilobytes)-1 ;
+
+#elif LL_LINUX
+ // mStatsMap is derived from MEMINFO_FILE:
+ // $ cat /proc/meminfo
+ // MemTotal: 4108424 kB
+ // MemFree: 1244064 kB
+ // Buffers: 85164 kB
+ // Cached: 1990264 kB
+ // SwapCached: 0 kB
+ // Active: 1176648 kB
+ // Inactive: 1427532 kB
+ // Active(anon): 529152 kB
+ // Inactive(anon): 15924 kB
+ // Active(file): 647496 kB
+ // Inactive(file): 1411608 kB
+ // Unevictable: 16 kB
+ // Mlocked: 16 kB
+ // HighTotal: 3266316 kB
+ // HighFree: 721308 kB
+ // LowTotal: 842108 kB
+ // LowFree: 522756 kB
+ // SwapTotal: 6384632 kB
+ // SwapFree: 6384632 kB
+ // Dirty: 28 kB
+ // Writeback: 0 kB
+ // AnonPages: 528820 kB
+ // Mapped: 89472 kB
+ // Shmem: 16324 kB
+ // Slab: 159624 kB
+ // SReclaimable: 145168 kB
+ // SUnreclaim: 14456 kB
+ // KernelStack: 2560 kB
+ // PageTables: 5560 kB
+ // NFS_Unstable: 0 kB
+ // Bounce: 0 kB
+ // WritebackTmp: 0 kB
+ // CommitLimit: 8438844 kB
+ // Committed_AS: 1271596 kB
+ // VmallocTotal: 122880 kB
+ // VmallocUsed: 65252 kB
+ // VmallocChunk: 52356 kB
+ // HardwareCorrupted: 0 kB
+ // HugePages_Total: 0
+ // HugePages_Free: 0
+ // HugePages_Rsvd: 0
+ // HugePages_Surp: 0
+ // Hugepagesize: 2048 kB
+ // DirectMap4k: 434168 kB
+ // DirectMap2M: 477184 kB
+ // (could also run 'free', but easier to read a file than run a program)
+ avail_physical_mem_kb = (U32Kilobytes)-1 ;
+ avail_virtual_mem_kb = (U32Kilobytes)-1 ;
+
+#else
+ //do not know how to collect available memory info for other systems.
+ //leave it blank here for now.
+
+ avail_physical_mem_kb = (U32Kilobytes)-1 ;
+ avail_virtual_mem_kb = (U32Kilobytes)-1 ;
+#endif
+}
+
void LLMemoryInfo::stream(std::ostream& s) const
{
+ // We want these memory stats to be easy to grep from the log, along with
+ // the timestamp. So preface each line with the timestamp and a
+ // distinctive marker. Without that, we'd have to search the log for the
+ // introducer line, then read subsequent lines, etc...
+ std::string pfx(LLError::utcTime() + " <mem> ");
+
+ // Max key length
+ size_t key_width(0);
+ BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
+ {
+ size_t len(pair.first.length());
+ if (len > key_width)
+ {
+ key_width = len;
+ }
+ }
+
+ // Now stream stats
+ BOOST_FOREACH(const MapEntry& pair, inMap(mStatsMap))
+ {
+ s << pfx << std::setw(key_width+1) << (pair.first + ':') << ' ';
+ LLSD value(pair.second);
+ if (value.isInteger())
+ s << std::setw(12) << value.asInteger();
+ else if (value.isReal())
+ s << std::fixed << std::setprecision(1) << value.asReal();
+ else if (value.isDate())
+ value.asDate().toStream(s);
+ else
+ s << value; // just use default LLSD formatting
+ s << std::endl;
+ }
+}
+
+LLSD LLMemoryInfo::getStatsMap() const
+{
+ return mStatsMap;
+}
+
+LLMemoryInfo& LLMemoryInfo::refresh()
+{
+ mStatsMap = loadStatsMap();
+
+ LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";
+ LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT);
+ LL_ENDL;
+
+ return *this;
+}
+
+LLSD LLMemoryInfo::loadStatsMap()
+{
+ // This implementation is derived from stream() code (as of 2011-06-29).
+ Stats stats;
+
+ // associate timestamp for analysis over time
+ stats.add("timestamp", LLDate::now());
+
#if LL_WINDOWS
MEMORYSTATUSEX state;
state.dwLength = sizeof(state);
GlobalMemoryStatusEx(&state);
- s << "Percent Memory use: " << (U32)state.dwMemoryLoad << '%' << std::endl;
- s << "Total Physical KB: " << (U32)(state.ullTotalPhys/1024) << std::endl;
- s << "Avail Physical KB: " << (U32)(state.ullAvailPhys/1024) << std::endl;
- s << "Total page KB: " << (U32)(state.ullTotalPageFile/1024) << std::endl;
- s << "Avail page KB: " << (U32)(state.ullAvailPageFile/1024) << std::endl;
- s << "Total Virtual KB: " << (U32)(state.ullTotalVirtual/1024) << std::endl;
- s << "Avail Virtual KB: " << (U32)(state.ullAvailVirtual/1024) << std::endl;
+ DWORDLONG div = 1024;
+
+ stats.add("Percent Memory use", state.dwMemoryLoad/div);
+ stats.add("Total Physical KB", state.ullTotalPhys/div);
+ stats.add("Avail Physical KB", state.ullAvailPhys/div);
+ stats.add("Total page KB", state.ullTotalPageFile/div);
+ stats.add("Avail page KB", state.ullAvailPageFile/div);
+ stats.add("Total Virtual KB", state.ullTotalVirtual/div);
+ stats.add("Avail Virtual KB", state.ullAvailVirtual/div);
+
+ PERFORMANCE_INFORMATION perf;
+ perf.cb = sizeof(perf);
+ GetPerformanceInfo(&perf, sizeof(perf));
+
+ SIZE_T pagekb(perf.PageSize/1024);
+ stats.add("CommitTotal KB", perf.CommitTotal * pagekb);
+ stats.add("CommitLimit KB", perf.CommitLimit * pagekb);
+ stats.add("CommitPeak KB", perf.CommitPeak * pagekb);
+ stats.add("PhysicalTotal KB", perf.PhysicalTotal * pagekb);
+ stats.add("PhysicalAvail KB", perf.PhysicalAvailable * pagekb);
+ stats.add("SystemCache KB", perf.SystemCache * pagekb);
+ stats.add("KernelTotal KB", perf.KernelTotal * pagekb);
+ stats.add("KernelPaged KB", perf.KernelPaged * pagekb);
+ stats.add("KernelNonpaged KB", perf.KernelNonpaged * pagekb);
+ stats.add("PageSize KB", pagekb);
+ stats.add("HandleCount", perf.HandleCount);
+ stats.add("ProcessCount", perf.ProcessCount);
+ stats.add("ThreadCount", perf.ThreadCount);
+
+ PROCESS_MEMORY_COUNTERS_EX pmem;
+ pmem.cb = sizeof(pmem);
+ // GetProcessMemoryInfo() is documented to accept either
+ // PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably
+ // using the redundant size info to distinguish. But its prototype
+ // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a
+ // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the
+ // pointer.
+ GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem));
+
+ stats.add("Page Fault Count", pmem.PageFaultCount);
+ stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div);
+ stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div);
+ stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div);
+ stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div);
+ stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div);
+ stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div);
+ stats.add("PagefileUsage KB", pmem.PagefileUsage/div);
+ stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div);
+ stats.add("PrivateUsage KB", pmem.PrivateUsage/div);
+
#elif LL_DARWIN
- uint64_t phys = 0;
- size_t len = sizeof(phys);
+ const vm_size_t pagekb(vm_page_size / 1024);
+
+ //
+ // Collect the vm_stat's
+ //
- if(sysctlbyname("hw.memsize", &phys, &len, NULL, 0) == 0)
{
- s << "Total Physical KB: " << phys/1024 << std::endl;
- }
- else
+ vm_statistics_data_t vmstat;
+ mach_msg_type_number_t vmstatCount = HOST_VM_INFO_COUNT;
+
+ if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t) &vmstat, &vmstatCount) != KERN_SUCCESS)
{
- s << "Unable to collect memory information";
+ LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
+ }
+ else
+ {
+ stats.add("Pages free KB", pagekb * vmstat.free_count);
+ stats.add("Pages active KB", pagekb * vmstat.active_count);
+ stats.add("Pages inactive KB", pagekb * vmstat.inactive_count);
+ stats.add("Pages wired KB", pagekb * vmstat.wire_count);
+
+ stats.add("Pages zero fill", vmstat.zero_fill_count);
+ stats.add("Page reactivations", vmstat.reactivations);
+ stats.add("Page-ins", vmstat.pageins);
+ stats.add("Page-outs", vmstat.pageouts);
+
+ stats.add("Faults", vmstat.faults);
+ stats.add("Faults copy-on-write", vmstat.cow_faults);
+
+ stats.add("Cache lookups", vmstat.lookups);
+ stats.add("Cache hits", vmstat.hits);
+
+ stats.add("Page purgeable count", vmstat.purgeable_count);
+ stats.add("Page purges", vmstat.purges);
+
+ stats.add("Page speculative reads", vmstat.speculative_count);
+ }
+ }
+
+ //
+ // Collect the misc task info
+ //
+
+ {
+ task_events_info_data_t taskinfo;
+ unsigned taskinfoSize = sizeof(taskinfo);
+
+ if (task_info(mach_task_self(), TASK_EVENTS_INFO, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS)
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL;
+ }
+ else
+ {
+ stats.add("Task page-ins", taskinfo.pageins);
+ stats.add("Task copy-on-write faults", taskinfo.cow_faults);
+ stats.add("Task messages sent", taskinfo.messages_sent);
+ stats.add("Task messages received", taskinfo.messages_received);
+ stats.add("Task mach system call count", taskinfo.syscalls_mach);
+ stats.add("Task unix system call count", taskinfo.syscalls_unix);
+ stats.add("Task context switch count", taskinfo.csw);
+ }
+ }
+
+ //
+ // Collect the basic task info
+ //
+
+ {
+ task_basic_info_64_data_t taskinfo;
+ unsigned taskinfoSize = sizeof(taskinfo);
+
+ if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS)
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL;
+ }
+ else
+ {
+ stats.add("Basic suspend count", taskinfo.suspend_count);
+ stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024);
+ stats.add("Basic resident memory KB", taskinfo.resident_size / 1024);
+ stats.add("Basic new thread policy", taskinfo.policy);
+ }
}
+
#elif LL_SOLARIS
- U64 phys = 0;
+ U64 phys = 0;
- phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
+ phys = (U64)(sysconf(_SC_PHYS_PAGES)) * (U64)(sysconf(_SC_PAGESIZE)/1024);
- s << "Total Physical KB: " << phys << std::endl;
-#else
- // *NOTE: This works on linux. What will it do on other systems?
- LLFILE* meminfo = LLFile::fopen(MEMINFO_FILE,"rb");
- if(meminfo)
+ stats.add("Total Physical KB", phys);
+
+#elif LL_LINUX
+ std::ifstream meminfo(MEMINFO_FILE);
+ if (meminfo.is_open())
{
- char line[MAX_STRING]; /* Flawfinder: ignore */
- memset(line, 0, MAX_STRING);
- while(fgets(line, MAX_STRING, meminfo))
+ // MemTotal: 4108424 kB
+ // MemFree: 1244064 kB
+ // Buffers: 85164 kB
+ // Cached: 1990264 kB
+ // SwapCached: 0 kB
+ // Active: 1176648 kB
+ // Inactive: 1427532 kB
+ // ...
+ // VmallocTotal: 122880 kB
+ // VmallocUsed: 65252 kB
+ // VmallocChunk: 52356 kB
+ // HardwareCorrupted: 0 kB
+ // HugePages_Total: 0
+ // HugePages_Free: 0
+ // HugePages_Rsvd: 0
+ // HugePages_Surp: 0
+ // Hugepagesize: 2048 kB
+ // DirectMap4k: 434168 kB
+ // DirectMap2M: 477184 kB
+
+ // Intentionally don't pass the boost::no_except flag. This
+ // boost::regex object is constructed with a string literal, so it
+ // should be valid every time. If it becomes invalid, we WANT an
+ // exception, hopefully even before the dev checks in.
+ boost::regex stat_rx("(.+): +([0-9]+)( kB)?");
+ boost::smatch matched;
+
+ std::string line;
+ while (std::getline(meminfo, line))
{
- line[strlen(line)-1] = ' '; /*Flawfinder: ignore*/
- s << line;
+ LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
+ if (regex_match_no_exc(line, matched, stat_rx))
+ {
+ // e.g. "MemTotal: 4108424 kB"
+ LLSD::String key(matched[1].first, matched[1].second);
+ LLSD::String value_str(matched[2].first, matched[2].second);
+ LLSD::Integer value(0);
+ try
+ {
+ value = boost::lexical_cast<LLSD::Integer>(value_str);
+ }
+ catch (const boost::bad_lexical_cast&)
+ {
+ LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+ << "' in " << MEMINFO_FILE << " line: "
+ << line << LL_ENDL;
+ continue;
+ }
+ // Store this statistic.
+ stats.add(key, value);
+ }
+ else
+ {
+ LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: "
+ << line << LL_ENDL;
+ }
}
- fclose(meminfo);
}
else
{
- s << "Unable to collect memory information";
+ LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
}
+
+#else
+ LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL;
+
#endif
+
+ return stats.get();
}
std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
@@ -793,6 +1321,148 @@ std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
return s;
}
+class FrameWatcher
+{
+public:
+ FrameWatcher():
+ // Hooking onto the "mainloop" event pump gets us one call per frame.
+ mConnection(LLEventPumps::instance()
+ .obtain("mainloop")
+ .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))),
+ // Initializing mSampleStart to an invalid timestamp alerts us to skip
+ // trying to compute framerate on the first call.
+ mSampleStart(-1),
+ // Initializing mSampleEnd to 0 ensures that we treat the first call
+ // as the completion of a sample window.
+ mSampleEnd(0),
+ mFrames(0),
+ // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need
+ // the number of integer MEM_INFO_THROTTLE sample slots that will fit
+ // in MEM_INFO_WINDOW. Round up.
+ mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)),
+ // Initializing to F32_MAX means that the first real frame will become
+ // the slowest ever, which sounds like a good idea.
+ mSlowest(F32_MAX)
+ {}
+
+ bool tick(const LLSD&)
+ {
+ F32 timestamp(mTimer.getElapsedTimeF32());
+
+ // Count this frame in the interval just completed.
+ ++mFrames;
+
+ // Have we finished a sample window yet?
+ if (timestamp < mSampleEnd)
+ {
+ // no, just keep waiting
+ return false;
+ }
+
+ // Set up for next sample window. Capture values for previous frame in
+ // local variables and reset data members.
+ U32 frames(mFrames);
+ F32 sampleStart(mSampleStart);
+ // No frames yet in next window
+ mFrames = 0;
+ // which starts right now
+ mSampleStart = timestamp;
+ // and ends MEM_INFO_THROTTLE seconds in the future
+ mSampleEnd = mSampleStart + MEM_INFO_THROTTLE;
+
+ // On the very first call, that's all we can do, no framerate
+ // computation is possible.
+ if (sampleStart < 0)
+ {
+ return false;
+ }
+
+ // How long did this actually take? As framerate slows, the duration
+ // of the frame we just finished could push us WELL beyond our desired
+ // sample window size.
+ F32 elapsed(timestamp - sampleStart);
+ F32 framerate(frames/elapsed);
+
+ // Remember previous slowest framerate because we're just about to
+ // update it.
+ F32 slowest(mSlowest);
+ // Remember previous number of samples.
+ boost::circular_buffer<F32>::size_type prevSize(mSamples.size());
+
+ // Capture new framerate in our samples buffer. Once the buffer is
+ // full (after MEM_INFO_WINDOW seconds), this will displace the oldest
+ // sample. ("So they all rolled over, and one fell out...")
+ mSamples.push_back(framerate);
+
+ // Calculate the new minimum framerate. I know of no way to update a
+ // rolling minimum without ever rescanning the buffer. But since there
+ // are only a few tens of items in this buffer, rescanning it is
+ // probably cheaper (and certainly easier to reason about) than
+ // attempting to optimize away some of the scans.
+ mSlowest = framerate; // pick an arbitrary entry to start
+ for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end());
+ si != send; ++si)
+ {
+ if (*si < mSlowest)
+ {
+ mSlowest = *si;
+ }
+ }
+
+ // We're especially interested in memory as framerate drops. Only log
+ // when framerate drops below the slowest framerate we remember.
+ // (Should always be true for the end of the very first sample
+ // window.)
+ if (framerate >= slowest)
+ {
+ return false;
+ }
+ // Congratulations, we've hit a new low. :-P
+
+ LL_INFOS("FrameWatcher") << ' ';
+ if (! prevSize)
+ {
+ LL_CONT << "initial framerate ";
+ }
+ else
+ {
+ LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE)
+ << " seconds ";
+ }
+
+ S32 precision = LL_CONT.precision();
+
+ LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n'
+ << LLMemoryInfo();
+
+ LL_CONT.precision(precision);
+ LL_CONT << LL_ENDL;
+ return false;
+ }
+
+private:
+ // Storing the connection in an LLTempBoundListener ensures it will be
+ // disconnected when we're destroyed.
+ LLTempBoundListener mConnection;
+ // Track elapsed time
+ LLTimer mTimer;
+ // Some of what you see here is in fact redundant with functionality you
+ // can get from LLTimer. Unfortunately the LLTimer API is missing the
+ // feature we need: has at least the stated interval elapsed, and if so,
+ // exactly how long has passed? So we have to do it by hand, sigh.
+ // Time at start, end of sample window
+ F32 mSampleStart, mSampleEnd;
+ // Frames this sample window
+ U32 mFrames;
+ // Sliding window of framerate samples
+ boost::circular_buffer<F32> mSamples;
+ // Slowest framerate in mSamples
+ F32 mSlowest;
+};
+
+// Need an instance of FrameWatcher before it does any good
+static FrameWatcher sFrameWatcher;
+
BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile)
{
std::string tmpfile;
@@ -813,7 +1483,7 @@ BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile)
size_t nwrit = fwrite(buffer, sizeof(U8), bytes, dst);
if (nwrit < (size_t) bytes)
{
- llwarns << "Short write on " << tmpfile << ": Wrote " << nwrit << " of " << bytes << " bytes." << llendl;
+ LL_WARNS() << "Short write on " << tmpfile << ": Wrote " << nwrit << " of " << bytes << " bytes." << LL_ENDL;
goto err;
}
} while(gzeof(src) == 0);
@@ -842,11 +1512,21 @@ BOOL gzip_file(const std::string& srcfile, const std::string& dstfile)
src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */
if (! src) goto err;
- do
+ while ((bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE, src)) > 0)
{
- bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE,src);
- gzwrite(dst, buffer, bytes);
- } while(feof(src) == 0);
+ if (gzwrite(dst, buffer, bytes) <= 0)
+ {
+ LL_WARNS() << "gzwrite failed: " << gzerror(dst, NULL) << LL_ENDL;
+ goto err;
+ }
+ }
+
+ if (ferror(src))
+ {
+ LL_WARNS() << "Error reading " << srcfile << LL_ENDL;
+ goto err;
+ }
+
gzclose(dst);
dst = NULL;
#if LL_WINDOWS
@@ -860,3 +1540,10 @@ BOOL gzip_file(const std::string& srcfile, const std::string& dstfile)
if (dst != NULL) gzclose(dst);
return retval;
}
+
+#if LL_DARWIN
+// disable warnings about Gestalt calls being deprecated
+// until Apple get's on the ball and provides an alternative
+//
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
index c2c45bec9a..962367f69f 100644..100755
--- a/indra/llcommon/llsys.h
+++ b/indra/llcommon/llsys.h
@@ -2,31 +2,25 @@
* @file llsys.h
* @brief System information debugging classes.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -39,9 +33,10 @@
// use an LLCPUInfo object:
//
// LLCPUInfo info;
-// llinfos << info << llendl;
+// LL_INFOS() << info << LL_ENDL;
//
+#include "llsd.h"
#include <iosfwd>
#include <string>
@@ -54,6 +49,8 @@ public:
const std::string& getOSString() const;
const std::string& getOSStringSimple() const;
+ const std::string& getOSVersionString() const;
+
S32 mMajorVer;
S32 mMinorVer;
S32 mBuild;
@@ -67,6 +64,7 @@ public:
private:
std::string mOSString;
std::string mOSStringSimple;
+ std::string mOSVersionString;
};
@@ -81,7 +79,7 @@ public:
bool hasAltivec() const;
bool hasSSE() const;
bool hasSSE2() const;
- S32 getMhz() const;
+ F64 getMHz() const;
// Family is "AMD Duron" or "Intel Pentium Pro"
const std::string& getFamily() const { return mFamily; }
@@ -90,7 +88,7 @@ private:
bool mHasSSE;
bool mHasSSE2;
bool mHasAltivec;
- S32 mCPUMhz;
+ F64 mCPUMHz;
std::string mFamily;
std::string mCPUString;
};
@@ -107,19 +105,43 @@ class LL_COMMON_API LLMemoryInfo
Here's how you use an LLMemoryInfo:
LLMemoryInfo info;
-<br> llinfos << info << llendl;
+<br> LL_INFOS() << info << LL_ENDL;
*/
{
public:
LLMemoryInfo(); ///< Default constructor
void stream(std::ostream& s) const; ///< output text info to s
- U32 getPhysicalMemoryKB() const; ///< Memory size in KiloBytes
+ U32Kilobytes getPhysicalMemoryKB() const;
/*! Memory size in bytes, if total memory is >= 4GB then U32_MAX will
** be returned.
*/
- U32 getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes
+ U32Bytes getPhysicalMemoryClamped() const; ///< Memory size in clamped bytes
+
+ //get the available memory infomation in KiloBytes.
+ static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb);
+
+ // Retrieve a map of memory statistics. The keys of the map are platform-
+ // dependent. The values are in kilobytes to try to avoid integer overflow.
+ LLSD getStatsMap() const;
+
+ // Re-fetch memory data (as reported by stream() and getStatsMap()) from the
+ // system. Normally this is fetched at construction time. Return (*this)
+ // to permit usage of the form:
+ // @code
+ // LLMemoryInfo info;
+ // ...
+ // info.refresh().getStatsMap();
+ // @endcode
+ LLMemoryInfo& refresh();
+
+private:
+ // set mStatsMap
+ static LLSD loadStatsMap();
+
+ // Memory stats for getStatsMap().
+ LLSD mStatsMap;
};
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index 37370e44e7..c3f235c6ee 100644..100755
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -1,31 +1,25 @@
/**
* @file llthread.cpp
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010-2013, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -35,13 +29,49 @@
#include "apr_portable.h"
#include "llthread.h"
+#include "llmutex.h"
#include "lltimer.h"
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
#if LL_LINUX || LL_SOLARIS
#include <sched.h>
#endif
+
+#ifdef LL_WINDOWS
+const DWORD MS_VC_EXCEPTION=0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+void set_thread_name( DWORD dwThreadID, const char* threadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = threadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+#endif
+
+
//----------------------------------------------------------------------------
// Usage:
// void run_func(LLThread* thread)
@@ -62,6 +92,26 @@
//
//----------------------------------------------------------------------------
+U32 LL_THREAD_LOCAL sThreadID = 0;
+
+U32 LLThread::sIDIter = 0;
+
+
+LL_COMMON_API void assert_main_thread()
+{
+ static U32 s_thread_id = LLThread::currentID();
+ if (LLThread::currentID() != s_thread_id)
+ {
+ LL_WARNS() << "Illegal execution from thread id " << (S32) LLThread::currentID()
+ << " outside main thread " << (S32) s_thread_id << LL_ENDL;
+ }
+}
+
+void LLThread::registerThreadID()
+{
+ sThreadID = ++sIDIter;
+}
+
//
// Handed to the APR thread creation function
//
@@ -69,27 +119,40 @@ 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;
+#ifdef LL_WINDOWS
+ set_thread_name(-1, threadp->mName.c_str());
+#endif
+
+ // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread
+ threadp->mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder());
+
+ sThreadID = threadp->mID;
// Run the user supplied function
threadp->run();
- llinfos << "LLThread::staticRun() Exiting: " << threadp->mName << llendl;
+ //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL;
+
+ delete threadp->mRecorder;
+ threadp->mRecorder = NULL;
// We're done with the run function, this thread is done executing now.
+ //NB: we are using this flag to sync across threads...we really need memory barriers here
threadp->mStatus = STOPPED;
return NULL;
}
-
LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
mPaused(FALSE),
mName(name),
mAPRThreadp(NULL),
- mStatus(STOPPED)
+ mStatus(STOPPED),
+ mRecorder(NULL)
{
+
+ mID = ++sIDIter;
+
// Thread creation probably CAN be paranoid about APR being initialized, if necessary
if (poolp)
{
@@ -102,7 +165,7 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
apr_pool_create(&mAPRPoolp, NULL); // Create a subpool for this thread
}
mRunCondition = new LLCondition(mAPRPoolp);
-
+ mDataLock = new LLMutex(mAPRPoolp);
mLocalAPRFilePoolp = NULL ;
}
@@ -130,10 +193,10 @@ void LLThread::shutdown()
// First, set the flag that indicates that we're ready to die
setQuitting();
- llinfos << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << llendl;
+ //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL;
// 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...
+ // should never get called unless we're already stopped, really...
S32 counter = 0;
const S32 MAX_WAIT = 600;
while (counter < MAX_WAIT)
@@ -152,27 +215,60 @@ void LLThread::shutdown()
if (!isStopped())
{
// This thread just wouldn't stop, even though we gave it time
- llwarns << "LLThread::~LLThread() exiting thread before clean exit!" << llendl;
+ //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL;
+ // Put a stake in its heart.
+ delete mRecorder;
+
+ apr_thread_exit(mAPRThreadp, -1);
return;
}
mAPRThreadp = NULL;
}
delete mRunCondition;
+ mRunCondition = NULL;
+
+ delete mDataLock;
+ mDataLock = NULL;
- if (mIsLocalPool)
+ if (mIsLocalPool && mAPRPoolp)
{
apr_pool_destroy(mAPRPoolp);
+ mAPRPoolp = 0;
+ }
+
+ if (mRecorder)
+ {
+ // missed chance to properly shut down recorder (needs to be done in thread context)
+ // probably due to abnormal thread termination
+ // so just leak it and remove it from parent
+ LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder);
}
}
void LLThread::start()
{
- apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
+ llassert(isStopped());
+
+ // Set thread state to running
+ mStatus = RUNNING;
+
+ apr_status_t status =
+ apr_thread_create(&mAPRThreadp, NULL, staticRun, (void *)this, mAPRPoolp);
+
+ if(status == APR_SUCCESS)
+ {
+ // We won't bother joining
+ apr_thread_detach(mAPRThreadp);
+ }
+ else
+ {
+ mStatus = STOPPED;
+ LL_WARNS() << "failed to start thread " << mName << LL_ENDL;
+ ll_apr_warn_status(status);
+ }
- // We won't bother joining
- apr_thread_detach(mAPRThreadp);
}
//============================================================================
@@ -211,35 +307,37 @@ bool LLThread::runCondition(void)
// Stop thread execution if requested until unpaused.
void LLThread::checkPause()
{
- mRunCondition->lock();
+ mDataLock->lock();
// This is in a while loop because the pthread API allows for spurious wakeups.
while(shouldSleep())
{
+ mDataLock->unlock();
mRunCondition->wait(); // unlocks mRunCondition
+ mDataLock->lock();
// mRunCondition is locked when the thread wakes up
}
- mRunCondition->unlock();
+ mDataLock->unlock();
}
//============================================================================
void LLThread::setQuitting()
{
- mRunCondition->lock();
+ mDataLock->lock();
if (mStatus == RUNNING)
{
mStatus = QUITTING;
}
- mRunCondition->unlock();
+ mDataLock->unlock();
wake();
}
// static
U32 LLThread::currentID()
{
- return (U32)apr_os_thread_current();
+ return sThreadID;
}
// static
@@ -254,12 +352,12 @@ void LLThread::yield()
void LLThread::wake()
{
- mRunCondition->lock();
+ mDataLock->lock();
if(!shouldSleep())
{
mRunCondition->signal();
}
- mRunCondition->unlock();
+ mDataLock->unlock();
}
void LLThread::wakeLocked()
@@ -272,110 +370,6 @@ void LLThread::wakeLocked()
//============================================================================
-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_UNNESTED, mAPRPoolp);
-}
-
-
-LLMutex::~LLMutex()
-{
-#if MUTEX_DEBUG
- llassert_always(!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);
-#if MUTEX_DEBUG
- // Have to have the lock before we can access the debug info
- U32 id = LLThread::currentID();
- if (mIsLocked[id] != FALSE)
- llerrs << "Already locked in Thread: " << id << llendl;
- mIsLocked[id] = TRUE;
-#endif
-}
-
-void LLMutex::unlock()
-{
-#if MUTEX_DEBUG
- // Access the debug info while we have the lock
- U32 id = LLThread::currentID();
- if (mIsLocked[id] != TRUE)
- llerrs << "Not locked in Thread: " << id << llendl;
- mIsLocked[id] = FALSE;
-#endif
- 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);
-}
-
-//============================================================================
-
//----------------------------------------------------------------------------
//static
@@ -405,11 +399,16 @@ LLThreadSafeRefCount::LLThreadSafeRefCount() :
{
}
+LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src)
+{
+ mRef = 0;
+}
+
LLThreadSafeRefCount::~LLThreadSafeRefCount()
{
if (mRef != 0)
{
- llerrs << "deleting non-zero reference" << llendl;
+ LL_ERRS() << "deleting non-zero reference" << LL_ENDL;
}
}
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index adef1a9192..6f9ec10fd3 100644..100755
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -2,31 +2,25 @@
* @file llthread.h
* @brief Base classes for thread, mutex and condition handling.
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010-2013, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -36,13 +30,23 @@
#include "llapp.h"
#include "llapr.h"
#include "apr_thread_cond.h"
+#include "boost/intrusive_ptr.hpp"
+#include "llmutex.h"
+#include "llrefcount.h"
+
+LL_COMMON_API void assert_main_thread();
-class LLThread;
-class LLMutex;
-class LLCondition;
+namespace LLTrace
+{
+ class ThreadRecorder;
+}
class LL_COMMON_API LLThread
{
+private:
+ friend class LLMutex;
+ static U32 sIDIter;
+
public:
typedef enum e_thread_status
{
@@ -83,20 +87,30 @@ public:
apr_pool_t *getAPRPool() { return mAPRPoolp; }
LLVolatileAPRPool* getLocalAPRFilePool() { return mLocalAPRFilePoolp ; }
+ U32 getID() const { return mID; }
+
+ // Called by threads *not* created via LLThread to register some
+ // internal state used by LLMutex. You must call this once early
+ // in the running thread to prevent collisions with the main thread.
+ static void registerThreadID();
+
private:
BOOL mPaused;
// static function passed to APR thread creation routine
- static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap);
+ static void *APR_THREAD_FUNC staticRun(struct apr_thread_t *apr_threadp, void *datap);
protected:
std::string mName;
- LLCondition* mRunCondition;
+ class LLCondition* mRunCondition;
+ LLMutex* mDataLock;
apr_thread_t *mAPRThreadp;
apr_pool_t *mAPRPoolp;
BOOL mIsLocalPool;
EThreadStatus mStatus;
+ U32 mID;
+ LLTrace::ThreadRecorder* mRecorder;
//a local apr_pool for APRFile operations in this thread. If it exists, LLAPRFile::sAPRFilePoolp should not be used.
//Note: this pool is used by APRFile ONLY, do NOT use it for any other purposes.
@@ -116,138 +130,31 @@ protected:
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
+ // It should only be called with mDataLock 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();
+ // mDataLock->lock();
// if(!shouldSleep())
// mRunCondition->signal();
- // mRunCondition->unlock();
-};
-
-//============================================================================
-
-#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
-
-class LL_COMMON_API LLMutex
-{
-public:
- LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
- ~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;
-#if MUTEX_DEBUG
- std::map<U32, BOOL> mIsLocked;
-#endif
+ // mDataLock->unlock();
};
-// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
-class LL_COMMON_API 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();
+ mDataLock->lock();
}
void LLThread::unlockData()
{
- mRunCondition->unlock();
+ mDataLock->unlock();
}
//============================================================================
-// see llmemory.h for LLPointer<> definition
-
-class LL_COMMON_API LLThreadSafeRefCount
-{
-public:
- static void initThreadSafeRefCount(); // creates sMutex
- static void cleanupThreadSafeRefCount(); // 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;
- return 0;
- }
- return res;
- }
- S32 getNumRefs() const
- {
- return mRef;
- }
-
-private:
- S32 mRef;
-};
-
-//============================================================================
-
// Simple responder for self destructing callbacks
// Pure virtual class
class LL_COMMON_API LLResponder : public LLThreadSafeRefCount
@@ -260,4 +167,6 @@ public:
//============================================================================
+extern LL_COMMON_API void assert_main_thread();
+
#endif // LL_LLTHREAD_H
diff --git a/indra/llcommon/llthreadlocalstorage.cpp b/indra/llcommon/llthreadlocalstorage.cpp
new file mode 100644
index 0000000000..8cef05caac
--- /dev/null
+++ b/indra/llcommon/llthreadlocalstorage.cpp
@@ -0,0 +1,119 @@
+/**
+ * @file llthreadlocalstorage.cpp
+ * @author Richard
+ * @date 2013-1-11
+ * @brief implementation of thread local storage utility classes
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llthreadlocalstorage.h"
+#include "llapr.h"
+
+//
+//LLThreadLocalPointerBase
+//
+bool LLThreadLocalPointerBase::sInitialized = false;
+
+void LLThreadLocalPointerBase::set( void* value )
+{
+ llassert(sInitialized && mThreadKey);
+
+ apr_status_t result = apr_threadkey_private_set((void*)value, mThreadKey);
+ if (result != APR_SUCCESS)
+ {
+ ll_apr_warn_status(result);
+ LL_ERRS() << "Failed to set thread local data" << LL_ENDL;
+ }
+}
+
+void* LLThreadLocalPointerBase::get() const
+{
+ // llassert(sInitialized);
+ void* ptr;
+ apr_status_t result =
+ apr_threadkey_private_get(&ptr, mThreadKey);
+ if (result != APR_SUCCESS)
+ {
+ ll_apr_warn_status(result);
+ LL_ERRS() << "Failed to get thread local data" << LL_ENDL;
+ }
+ return ptr;
+}
+
+
+void LLThreadLocalPointerBase::initStorage( )
+{
+ apr_status_t result = apr_threadkey_private_create(&mThreadKey, NULL, gAPRPoolp);
+ if (result != APR_SUCCESS)
+ {
+ ll_apr_warn_status(result);
+ LL_ERRS() << "Failed to allocate thread local data" << LL_ENDL;
+ }
+}
+
+void LLThreadLocalPointerBase::destroyStorage()
+{
+ if (sInitialized)
+ {
+ if (mThreadKey)
+ {
+ apr_status_t result = apr_threadkey_private_delete(mThreadKey);
+ if (result != APR_SUCCESS)
+ {
+ ll_apr_warn_status(result);
+ LL_ERRS() << "Failed to delete thread local data" << LL_ENDL;
+ }
+ }
+ }
+}
+
+//static
+void LLThreadLocalPointerBase::initAllThreadLocalStorage()
+{
+ if (!sInitialized)
+ {
+ for (LLInstanceTracker<LLThreadLocalPointerBase>::instance_iter it = beginInstances(), end_it = endInstances();
+ it != end_it;
+ ++it)
+ {
+ (*it).initStorage();
+ }
+ sInitialized = true;
+ }
+}
+
+//static
+void LLThreadLocalPointerBase::destroyAllThreadLocalStorage()
+{
+ if (sInitialized)
+ {
+ //for (LLInstanceTracker<LLThreadLocalPointerBase>::instance_iter it = beginInstances(), end_it = endInstances();
+ // it != end_it;
+ // ++it)
+ //{
+ // (*it).destroyStorage();
+ //}
+ sInitialized = false;
+ }
+}
diff --git a/indra/llcommon/llthreadlocalstorage.h b/indra/llcommon/llthreadlocalstorage.h
new file mode 100644
index 0000000000..3b5786023f
--- /dev/null
+++ b/indra/llcommon/llthreadlocalstorage.h
@@ -0,0 +1,148 @@
+/**
+ * @file llthreadlocalstorage.h
+ * @author Richard
+ * @brief Class wrappers for thread local storage
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTHREADLOCALSTORAGE_H
+#define LL_LLTHREADLOCALSTORAGE_H
+
+#include "llinstancetracker.h"
+
+class LLThreadLocalPointerBase : public LLInstanceTracker<LLThreadLocalPointerBase>
+{
+public:
+ LLThreadLocalPointerBase()
+ : mThreadKey(NULL)
+ {
+ if (sInitialized)
+ {
+ initStorage();
+ }
+ }
+
+ LLThreadLocalPointerBase( const LLThreadLocalPointerBase& other)
+ : mThreadKey(NULL)
+ {
+ if (sInitialized)
+ {
+ initStorage();
+ }
+ }
+
+ ~LLThreadLocalPointerBase()
+ {
+ destroyStorage();
+ }
+
+ static void initAllThreadLocalStorage();
+ static void destroyAllThreadLocalStorage();
+
+protected:
+ void set(void* value);
+
+ void* get() const;
+
+ void initStorage();
+ void destroyStorage();
+
+protected:
+ struct apr_threadkey_t* mThreadKey;
+ static bool sInitialized;
+};
+
+template <typename T>
+class LLThreadLocalPointer : public LLThreadLocalPointerBase
+{
+public:
+
+ LLThreadLocalPointer()
+ {}
+
+ explicit LLThreadLocalPointer(T* value)
+ {
+ set(value);
+ }
+
+
+ LLThreadLocalPointer(const LLThreadLocalPointer<T>& other)
+ : LLThreadLocalPointerBase(other)
+ {
+ set(other.get());
+ }
+
+ LL_FORCE_INLINE T* get() const
+ {
+ return (T*)LLThreadLocalPointerBase::get();
+ }
+
+ T* operator -> () const
+ {
+ return (T*)get();
+ }
+
+ T& operator*() const
+ {
+ return *(T*)get();
+ }
+
+ LLThreadLocalPointer<T>& operator = (T* value)
+ {
+ set((void*)value);
+ return *this;
+ }
+
+ bool operator ==(const T* other) const
+ {
+ if (!sInitialized) return false;
+ return get() == other;
+ }
+
+ bool isNull() const { return !sInitialized || get() == NULL; }
+
+ bool notNull() const { return sInitialized && get() != NULL; }
+};
+
+template<typename DERIVED_TYPE>
+class LLThreadLocalSingletonPointer
+{
+public:
+ LL_FORCE_INLINE static DERIVED_TYPE* getInstance()
+ {
+ return sInstance;
+ }
+
+ static void setInstance(DERIVED_TYPE* instance)
+ {
+ sInstance = instance;
+ }
+
+private:
+ static LL_THREAD_LOCAL DERIVED_TYPE* sInstance;
+};
+
+template<typename DERIVED_TYPE>
+LL_THREAD_LOCAL DERIVED_TYPE* LLThreadLocalSingletonPointer<DERIVED_TYPE>::sInstance = NULL;
+
+#endif // LL_LLTHREADLOCALSTORAGE_H
diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp
new file mode 100755
index 0000000000..185f0d63fb
--- /dev/null
+++ b/indra/llcommon/llthreadsafequeue.cpp
@@ -0,0 +1,109 @@
+/**
+ * @file llthread.cpp
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include <apr_pools.h>
+#include <apr_queue.h>
+#include "llthreadsafequeue.h"
+
+
+
+// LLThreadSafeQueueImplementation
+//-----------------------------------------------------------------------------
+
+
+LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity):
+ mOwnsPool(pool == 0),
+ mPool(pool),
+ mQueue(0)
+{
+ if(mOwnsPool) {
+ apr_status_t status = apr_pool_create(&mPool, 0);
+ if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool");
+ } else {
+ ; // No op.
+ }
+
+ apr_status_t status = apr_queue_create(&mQueue, capacity, mPool);
+ if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue");
+}
+
+
+LLThreadSafeQueueImplementation::~LLThreadSafeQueueImplementation()
+{
+ if(mQueue != 0) {
+ if(apr_queue_size(mQueue) != 0) LL_WARNS() <<
+ "terminating queue which still contains " << apr_queue_size(mQueue) <<
+ " elements;" << "memory will be leaked" << LL_ENDL;
+ apr_queue_term(mQueue);
+ }
+ if(mOwnsPool && (mPool != 0)) apr_pool_destroy(mPool);
+}
+
+
+void LLThreadSafeQueueImplementation::pushFront(void * element)
+{
+ apr_status_t status = apr_queue_push(mQueue, element);
+
+ if(status == APR_EINTR) {
+ throw LLThreadSafeQueueInterrupt();
+ } else if(status != APR_SUCCESS) {
+ throw LLThreadSafeQueueError("push failed");
+ } else {
+ ; // Success.
+ }
+}
+
+
+bool LLThreadSafeQueueImplementation::tryPushFront(void * element){
+ return apr_queue_trypush(mQueue, element) == APR_SUCCESS;
+}
+
+
+void * LLThreadSafeQueueImplementation::popBack(void)
+{
+ void * element;
+ apr_status_t status = apr_queue_pop(mQueue, &element);
+
+ if(status == APR_EINTR) {
+ throw LLThreadSafeQueueInterrupt();
+ } else if(status != APR_SUCCESS) {
+ throw LLThreadSafeQueueError("pop failed");
+ } else {
+ return element;
+ }
+}
+
+
+bool LLThreadSafeQueueImplementation::tryPopBack(void *& element)
+{
+ return apr_queue_trypop(mQueue, &element) == APR_SUCCESS;
+}
+
+
+size_t LLThreadSafeQueueImplementation::size()
+{
+ return apr_queue_size(mQueue);
+}
diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
new file mode 100755
index 0000000000..58cac38769
--- /dev/null
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -0,0 +1,205 @@
+/**
+ * @file llthreadsafequeue.h
+ * @brief Base classes for thread, mutex and condition handling.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTHREADSAFEQUEUE_H
+#define LL_LLTHREADSAFEQUEUE_H
+
+
+#include <string>
+#include <stdexcept>
+
+
+struct apr_pool_t; // From apr_pools.h
+class LLThreadSafeQueueImplementation; // See below.
+
+
+//
+// A general queue exception.
+//
+class LL_COMMON_API LLThreadSafeQueueError:
+public std::runtime_error
+{
+public:
+ LLThreadSafeQueueError(std::string const & message):
+ std::runtime_error(message)
+ {
+ ; // No op.
+ }
+};
+
+
+//
+// An exception raised when blocking operations are interrupted.
+//
+class LL_COMMON_API LLThreadSafeQueueInterrupt:
+ public LLThreadSafeQueueError
+{
+public:
+ LLThreadSafeQueueInterrupt(void):
+ LLThreadSafeQueueError("queue operation interrupted")
+ {
+ ; // No op.
+ }
+};
+
+
+struct apr_queue_t; // From apr_queue.h
+
+
+//
+// Implementation details.
+//
+class LL_COMMON_API LLThreadSafeQueueImplementation
+{
+public:
+ LLThreadSafeQueueImplementation(apr_pool_t * pool, unsigned int capacity);
+ ~LLThreadSafeQueueImplementation();
+ void pushFront(void * element);
+ bool tryPushFront(void * element);
+ void * popBack(void);
+ bool tryPopBack(void *& element);
+ size_t size();
+
+private:
+ bool mOwnsPool;
+ apr_pool_t * mPool;
+ apr_queue_t * mQueue;
+};
+
+
+//
+// Implements a thread safe FIFO.
+//
+template<typename ElementT>
+class LLThreadSafeQueue
+{
+public:
+ typedef ElementT value_type;
+
+ // If the pool is set to NULL one will be allocated and managed by this
+ // queue.
+ LLThreadSafeQueue(apr_pool_t * pool = 0, unsigned int capacity = 1024);
+
+ // Add an element to the front of queue (will block if the queue has
+ // reached capacity).
+ //
+ // This call will raise an interrupt error if the queue is deleted while
+ // the caller is blocked.
+ void pushFront(ElementT const & element);
+
+ // Try to add an element to the front ofqueue without blocking. Returns
+ // true only if the element was actually added.
+ bool tryPushFront(ElementT const & element);
+
+ // Pop the element at the end of the queue (will block if the queue is
+ // empty).
+ //
+ // This call will raise an interrupt error if the queue is deleted while
+ // the caller is blocked.
+ ElementT popBack(void);
+
+ // Pop an element from the end of the queue if there is one available.
+ // Returns true only if an element was popped.
+ bool tryPopBack(ElementT & element);
+
+ // Returns the size of the queue.
+ size_t size();
+
+private:
+ LLThreadSafeQueueImplementation mImplementation;
+};
+
+
+
+// LLThreadSafeQueue
+//-----------------------------------------------------------------------------
+
+
+template<typename ElementT>
+LLThreadSafeQueue<ElementT>::LLThreadSafeQueue(apr_pool_t * pool, unsigned int capacity):
+ mImplementation(pool, capacity)
+{
+ ; // No op.
+}
+
+
+template<typename ElementT>
+void LLThreadSafeQueue<ElementT>::pushFront(ElementT const & element)
+{
+ ElementT * elementCopy = new ElementT(element);
+ try {
+ mImplementation.pushFront(elementCopy);
+ } catch (LLThreadSafeQueueInterrupt) {
+ delete elementCopy;
+ throw;
+ }
+}
+
+
+template<typename ElementT>
+bool LLThreadSafeQueue<ElementT>::tryPushFront(ElementT const & element)
+{
+ ElementT * elementCopy = new ElementT(element);
+ bool result = mImplementation.tryPushFront(elementCopy);
+ if(!result) delete elementCopy;
+ return result;
+}
+
+
+template<typename ElementT>
+ElementT LLThreadSafeQueue<ElementT>::popBack(void)
+{
+ ElementT * element = reinterpret_cast<ElementT *> (mImplementation.popBack());
+ ElementT result(*element);
+ delete element;
+ return result;
+}
+
+
+template<typename ElementT>
+bool LLThreadSafeQueue<ElementT>::tryPopBack(ElementT & element)
+{
+ void * storedElement;
+ bool result = mImplementation.tryPopBack(storedElement);
+ if(result) {
+ ElementT * elementPtr = reinterpret_cast<ElementT *>(storedElement);
+ element = *elementPtr;
+ delete elementPtr;
+ } else {
+ ; // No op.
+ }
+ return result;
+}
+
+
+template<typename ElementT>
+size_t LLThreadSafeQueue<ElementT>::size(void)
+{
+ return mImplementation.size();
+}
+
+
+#endif
diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp
index 25b768079b..76e892212a 100644..100755
--- a/indra/llcommon/lltimer.cpp
+++ b/indra/llcommon/lltimer.cpp
@@ -2,31 +2,25 @@
* @file lltimer.cpp
* @brief Cross-platform objects for doing timing
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -37,25 +31,18 @@
#include "u64.h"
#if LL_WINDOWS
-# define WIN32_LEAN_AND_MEAN
-# include <winsock2.h>
-# include <windows.h>
+# include "llwin32headerslean.h"
#elif LL_LINUX || LL_SOLARIS || LL_DARWIN
-# include <errno.h>
+# include <errno.h>
# include <sys/time.h>
#else
# error "architecture not supported"
#endif
-
//
// 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
@@ -64,11 +51,6 @@ const F64 USEC_TO_SEC_F64 = 0.000001;
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
@@ -89,7 +71,7 @@ U32 micro_sleep(U64 us, U32 max_yields)
{
// max_yields is unused; just fiddle with it to avoid warnings.
max_yields = 0;
- ms_sleep(us / 1000);
+ ms_sleep((U32)(us / 1000));
return 0;
}
#elif LL_LINUX || LL_SOLARIS || LL_DARWIN
@@ -196,7 +178,7 @@ U64 get_clock_count()
return clock_count.QuadPart - offset;
}
-F64 calc_clock_frequency(U32 uiMeasureMSecs)
+F64 calc_clock_frequency()
{
__int64 freq;
QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
@@ -207,9 +189,9 @@ F64 calc_clock_frequency(U32 uiMeasureMSecs)
#if LL_LINUX || LL_DARWIN || LL_SOLARIS
// Both Linux and Mac use gettimeofday for accurate time
-F64 calc_clock_frequency(unsigned int uiMeasureMSecs)
+F64 calc_clock_frequency()
{
- return 1000000.0; // microseconds, so 1 Mhz.
+ return 1000000.0; // microseconds, so 1 MHz.
}
U64 get_clock_count()
@@ -222,56 +204,68 @@ U64 get_clock_count()
#endif
-void update_clock_frequencies()
+TimerInfo::TimerInfo()
+: mClockFrequency(0.0),
+ mTotalTimeClockCount(0),
+ mLastTotalTimeClockCount(0)
+{}
+
+void TimerInfo::update()
{
- gClockFrequency = calc_clock_frequency(50U);
- gClockFrequencyInv = 1.0/gClockFrequency;
- gClocksToMicroseconds = gClockFrequencyInv * SEC_TO_MICROSEC;
+ mClockFrequency = calc_clock_frequency();
+ mClockFrequencyInv = 1.0/mClockFrequency;
+ mClocksToMicroseconds = mClockFrequencyInv;
}
+TimerInfo& get_timer_info()
+{
+ static TimerInfo sTimerInfo;
+ return sTimerInfo;
+}
///////////////////////////////////////////////////////////////////////////////
// returns a U64 number that represents the number of
-// microseconds since the unix epoch - Jan 1, 1970
-U64 totalTime()
+// microseconds since the Unix epoch - Jan 1, 1970
+U64MicrosecondsImplicit totalTime()
{
U64 current_clock_count = get_clock_count();
- if (!gTotalTimeClockCount)
+ if (!get_timer_info().mTotalTimeClockCount || get_timer_info().mClocksToMicroseconds.value() == 0)
{
- update_clock_frequencies();
- gTotalTimeClockCount = current_clock_count;
+ get_timer_info().update();
+ get_timer_info().mTotalTimeClockCount = 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)
+ // Sync 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);
+ get_timer_info().mTotalTimeClockCount = (U64)(time(NULL) * get_timer_info().mClockFrequency);
#endif
// Update the last clock count
- gLastTotalTimeClockCount = current_clock_count;
+ get_timer_info().mLastTotalTimeClockCount = current_clock_count;
}
else
{
- if (current_clock_count >= gLastTotalTimeClockCount)
+ if (current_clock_count >= get_timer_info().mLastTotalTimeClockCount)
{
// No wrapping, we're all okay.
- gTotalTimeClockCount += current_clock_count - gLastTotalTimeClockCount;
+ get_timer_info().mTotalTimeClockCount += current_clock_count - get_timer_info().mLastTotalTimeClockCount;
}
else
{
// We've wrapped. Compensate correctly
- gTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - gLastTotalTimeClockCount) + current_clock_count;
+ get_timer_info().mTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - get_timer_info().mLastTotalTimeClockCount) + current_clock_count;
}
// Update the last clock count
- gLastTotalTimeClockCount = current_clock_count;
+ get_timer_info().mLastTotalTimeClockCount = current_clock_count;
}
// Return the total clock tick count in microseconds.
- return (U64)(gTotalTimeClockCount*gClocksToMicroseconds);
+ U64Microseconds time(get_timer_info().mTotalTimeClockCount*get_timer_info().mClocksToMicroseconds);
+ return time;
}
@@ -279,9 +273,9 @@ U64 totalTime()
LLTimer::LLTimer()
{
- if (!gClockFrequency)
+ if (!get_timer_info().mClockFrequency)
{
- update_clock_frequencies();
+ get_timer_info().update();
}
mStarted = TRUE;
@@ -289,20 +283,32 @@ LLTimer::LLTimer()
}
LLTimer::~LLTimer()
+{}
+
+// static
+void LLTimer::initClass()
+{
+ if (!sTimer) sTimer = new LLTimer;
+}
+
+// static
+void LLTimer::cleanupClass()
{
+ delete sTimer; sTimer = NULL;
}
// static
-U64 LLTimer::getTotalTime()
+U64MicrosecondsImplicit LLTimer::getTotalTime()
{
// simply call into the implementation function.
- return totalTime();
+ U64MicrosecondsImplicit total_time = totalTime();
+ return total_time;
}
// static
-F64 LLTimer::getTotalSeconds()
+F64SecondsImplicit LLTimer::getTotalSeconds()
{
- return U64_to_F64(getTotalTime()) * USEC_TO_SEC_F64;
+ return F64Microseconds(U64_to_F64(getTotalTime()));
}
void LLTimer::reset()
@@ -349,43 +355,43 @@ U64 getElapsedTimeAndUpdate(U64& lastClockCount)
}
-F64 LLTimer::getElapsedTimeF64() const
+F64SecondsImplicit LLTimer::getElapsedTimeF64() const
{
U64 last = mLastClockCount;
- return (F64)getElapsedTimeAndUpdate(last) * gClockFrequencyInv;
+ return (F64)getElapsedTimeAndUpdate(last) * get_timer_info().mClockFrequencyInv;
}
-F32 LLTimer::getElapsedTimeF32() const
+F32SecondsImplicit LLTimer::getElapsedTimeF32() const
{
return (F32)getElapsedTimeF64();
}
-F64 LLTimer::getElapsedTimeAndResetF64()
+F64SecondsImplicit LLTimer::getElapsedTimeAndResetF64()
{
- return (F64)getElapsedTimeAndUpdate(mLastClockCount) * gClockFrequencyInv;
+ return (F64)getElapsedTimeAndUpdate(mLastClockCount) * get_timer_info().mClockFrequencyInv;
}
-F32 LLTimer::getElapsedTimeAndResetF32()
+F32SecondsImplicit LLTimer::getElapsedTimeAndResetF32()
{
return (F32)getElapsedTimeAndResetF64();
}
///////////////////////////////////////////////////////////////////////////////
-void LLTimer::setTimerExpirySec(F32 expiration)
+void LLTimer::setTimerExpirySec(F32SecondsImplicit expiration)
{
mExpirationTicks = get_clock_count()
- + (U64)((F32)(expiration * gClockFrequency));
+ + (U64)((F32)(expiration * get_timer_info().mClockFrequency.value()));
}
-F32 LLTimer::getRemainingTimeF32() const
+F32SecondsImplicit LLTimer::getRemainingTimeF32() const
{
U64 cur_ticks = get_clock_count();
if (cur_ticks > mExpirationTicks)
{
return 0.0f;
}
- return F32((mExpirationTicks - cur_ticks) * gClockFrequencyInv);
+ return F32((mExpirationTicks - cur_ticks) * get_timer_info().mClockFrequencyInv);
}
@@ -398,7 +404,7 @@ BOOL LLTimer::checkExpirationAndReset(F32 expiration)
}
mExpirationTicks = cur_ticks
- + (U64)((F32)(expiration * gClockFrequency));
+ + (U64)((F32)(expiration * get_timer_info().mClockFrequency));
return TRUE;
}
@@ -454,7 +460,7 @@ BOOL LLTimer::knownBadTimer()
{
if (!wcscmp(pci_id, bad_pci_list[check]))
{
-// llwarns << "unreliable PCI chipset found!! " << pci_id << endl;
+// LL_WARNS() << "unreliable PCI chipset found!! " << pci_id << endl;
failed = TRUE;
break;
}
@@ -497,20 +503,20 @@ BOOL is_daylight_savings()
struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time)
{
- S32 pacific_offset_hours;
+ S32Hours pacific_offset_hours;
if (pacific_daylight_time)
{
- pacific_offset_hours = 7;
+ pacific_offset_hours = S32Hours(7);
}
else
{
- pacific_offset_hours = 8;
+ pacific_offset_hours = S32Hours(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.
- utc_time -= pacific_offset_hours * MIN_PER_HOUR * SEC_PER_MIN;
+ utc_time -= S32SecondsImplicit(pacific_offset_hours);
// Internal buffer to PST/PDT (see above)
struct tm* internal_time = gmtime(&utc_time);
@@ -527,7 +533,7 @@ struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time)
}
-void microsecondsToTimecodeString(U64 current_time, std::string& tcstring)
+void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring)
{
U64 hours;
U64 minutes;
@@ -549,9 +555,9 @@ void microsecondsToTimecodeString(U64 current_time, std::string& tcstring)
}
-void secondsToTimecodeString(F32 current_time, std::string& tcstring)
+void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring)
{
- microsecondsToTimecodeString((U64)((F64)(SEC_TO_MICROSEC*current_time)), tcstring);
+ microsecondsToTimecodeString(current_time, tcstring);
}
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index baba95bfa1..ec70213447 100644..100755
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -2,31 +2,25 @@
* @file lltimer.h
* @brief Cross-platform objects for doing timing
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,6 +37,7 @@
#include <string>
#include <list>
// units conversions
+#include "llunits.h"
#ifndef USEC_PER_SEC
const U32 USEC_PER_SEC = 1000000;
#endif
@@ -61,27 +56,34 @@ public:
protected:
U64 mLastClockCount;
U64 mExpirationTicks;
- BOOL mStarted;
+ bool mStarted;
public:
LLTimer();
~LLTimer();
- static void initClass() { if (!sTimer) sTimer = new LLTimer; }
- static void cleanupClass() { delete sTimer; sTimer = NULL; }
+ static void initClass();
+ static void cleanupClass();
// Return a high precision number of seconds since the start of
// this application instance.
- static F64 getElapsedSeconds()
+ static F64SecondsImplicit getElapsedSeconds()
+ {
+ if (sTimer)
{
return sTimer->getElapsedTimeF64();
}
+ else
+ {
+ return 0;
+ }
+ }
// Return a high precision usec since epoch
- static U64 getTotalTime();
+ static U64MicrosecondsImplicit getTotalTime();
// Return a high precision seconds since epoch
- static F64 getTotalSeconds();
+ static F64SecondsImplicit getTotalSeconds();
// MANIPULATORS
@@ -89,21 +91,21 @@ public:
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);
+ void setTimerExpirySec(F32SecondsImplicit expiration);
BOOL checkExpirationAndReset(F32 expiration);
BOOL hasExpired() const;
- F32 getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
- F64 getElapsedTimeAndResetF64();
+ F32SecondsImplicit getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
+ F64SecondsImplicit getElapsedTimeAndResetF64();
- F32 getRemainingTimeF32() const;
+ F32SecondsImplicit getRemainingTimeF32() const;
static BOOL knownBadTimer();
// ACCESSORS
- F32 getElapsedTimeF32() const; // Returns elapsed time in seconds
- F64 getElapsedTimeF64() const; // Returns elapsed time in seconds
+ F32SecondsImplicit getElapsedTimeF32() const; // Returns elapsed time in seconds
+ F64SecondsImplicit getElapsedTimeF64() const; // Returns elapsed time in seconds
- BOOL getStarted() const { return mStarted; }
+ bool getStarted() const { return mStarted; }
static U64 getCurrentClockCount(); // Returns the raw clockticks
@@ -112,9 +114,21 @@ public:
//
// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
//
+struct TimerInfo
+{
+ TimerInfo();
+ void update();
+
+ F64HertzImplicit mClockFrequency;
+ F64SecondsImplicit mClockFrequencyInv;
+ F64MicrosecondsImplicit mClocksToMicroseconds;
+ U64 mTotalTimeClockCount;
+ U64 mLastTotalTimeClockCount;
+};
+
+TimerInfo& get_timer_info();
+
LL_COMMON_API U64 get_clock_count();
-LL_COMMON_API F64 calc_clock_frequency(U32 msecs);
-LL_COMMON_API void update_clock_frequencies();
// Sleep for milliseconds
LL_COMMON_API void ms_sleep(U32 ms);
@@ -166,9 +180,9 @@ LL_COMMON_API BOOL is_daylight_savings();
// struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight);
LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time);
-LL_COMMON_API void microsecondsToTimecodeString(U64 current_time, std::string& tcstring);
-LL_COMMON_API void secondsToTimecodeString(F32 current_time, std::string& tcstring);
+LL_COMMON_API void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring);
+LL_COMMON_API void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring);
-U64 LL_COMMON_API totalTime(); // Returns current system time in microseconds
+U64MicrosecondsImplicit LL_COMMON_API totalTime(); // Returns current system time in microseconds
#endif
diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp
new file mode 100644
index 0000000000..54079a4689
--- /dev/null
+++ b/indra/llcommon/lltrace.cpp
@@ -0,0 +1,86 @@
+/**
+ * @file lltrace.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrace.h"
+#include "lltracerecording.h"
+#include "lltracethreadrecorder.h"
+#include "llfasttimer.h"
+
+namespace LLTrace
+{
+
+MemStatHandle gTraceMemStat("LLTrace");
+
+StatBase::StatBase( const char* name, const char* description )
+: mName(name),
+ mDescription(description ? description : "")
+{
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ if (LLTrace::get_thread_recorder().notNull())
+ {
+ LL_ERRS() << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << LL_ENDL;
+ }
+#endif
+}
+
+const char* StatBase::getUnitLabel() const
+{
+ return "";
+}
+
+TimeBlockTreeNode::TimeBlockTreeNode()
+: mBlock(NULL),
+ mParent(NULL),
+ mNeedsSorting(false),
+ mCollapsed(true)
+{}
+
+void TimeBlockTreeNode::setParent( BlockTimerStatHandle* parent )
+{
+ llassert_always(parent != mBlock);
+ llassert_always(parent != NULL);
+
+ TimeBlockTreeNode* parent_tree_node = get_thread_recorder()->getTimeBlockTreeNode(parent->getIndex());
+ if (!parent_tree_node) return;
+
+ if (mParent)
+ {
+ std::vector<BlockTimerStatHandle*>& children = mParent->getChildren();
+ std::vector<BlockTimerStatHandle*>::iterator found_it = std::find(children.begin(), children.end(), mBlock);
+ if (found_it != children.end())
+ {
+ children.erase(found_it);
+ }
+ }
+
+ mParent = parent;
+ mBlock->getCurrentAccumulator().mParent = parent;
+ parent_tree_node->mChildren.push_back(mBlock);
+ parent_tree_node->mNeedsSorting = true;
+}
+
+}
diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h
new file mode 100644
index 0000000000..5f1289dad8
--- /dev/null
+++ b/indra/llcommon/lltrace.h
@@ -0,0 +1,492 @@
+/**
+ * @file lltrace.h
+ * @brief Runtime statistics accumulation.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTRACE_H
+#define LL_LLTRACE_H
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+
+#include "llmemory.h"
+#include "llrefcount.h"
+#include "lltraceaccumulators.h"
+#include "llthreadlocalstorage.h"
+#include "lltimer.h"
+#include "llpointer.h"
+#include "llunits.h"
+
+#define LL_TRACE_ENABLED 1
+
+namespace LLTrace
+{
+class Recording;
+
+template<typename T>
+T storage_value(T val) { return val; }
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE>
+STORAGE_TYPE storage_value(LLUnit<STORAGE_TYPE, UNIT_TYPE> val) { return val.value(); }
+
+template<typename UNIT_TYPE, typename STORAGE_TYPE>
+STORAGE_TYPE storage_value(LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> val) { return val.value(); }
+
+class StatBase
+{
+public:
+ StatBase(const char* name, const char* description);
+ virtual ~StatBase() {};
+ virtual const char* getUnitLabel() const;
+
+ const std::string& getName() const { return mName; }
+ const std::string& getDescription() const { return mDescription; }
+
+protected:
+ std::string mName;
+ std::string mDescription;
+};
+
+template<typename ACCUMULATOR>
+class StatType
+: public StatBase,
+ public LLInstanceTracker<StatType<ACCUMULATOR>, std::string>
+{
+public:
+ typedef LLInstanceTracker<StatType<ACCUMULATOR>, std::string> instance_tracker_t;
+ StatType(const char* name, const char* description)
+ : instance_tracker_t(name),
+ StatBase(name, description),
+ mAccumulatorIndex(AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer()->reserveSlot())
+ {}
+
+ LL_FORCE_INLINE ACCUMULATOR& getCurrentAccumulator() const
+ {
+ ACCUMULATOR* accumulator_storage = LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance();
+ return accumulator_storage ? accumulator_storage[mAccumulatorIndex] : (*AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer())[mAccumulatorIndex];
+ }
+
+ size_t getIndex() const { return mAccumulatorIndex; }
+ static size_t getNumIndices() { return AccumulatorBuffer<ACCUMULATOR>::getNumIndices(); }
+
+protected:
+ const size_t mAccumulatorIndex;
+};
+
+
+template<>
+class StatType<TimeBlockAccumulator::CallCountFacet>
+: public StatType<TimeBlockAccumulator>
+{
+public:
+
+ StatType(const char* name, const char* description = "")
+ : StatType<TimeBlockAccumulator>(name, description)
+ {}
+};
+
+template<>
+class StatType<TimeBlockAccumulator::SelfTimeFacet>
+ : public StatType<TimeBlockAccumulator>
+{
+public:
+
+ StatType(const char* name, const char* description = "")
+ : StatType<TimeBlockAccumulator>(name, description)
+ {}
+};
+
+template <typename T = F64>
+class EventStatHandle
+: public StatType<EventAccumulator>
+{
+public:
+ typedef F64 storage_t;
+ typedef StatType<EventAccumulator> stat_t;
+ typedef EventStatHandle<T> self_t;
+
+ EventStatHandle(const char* name, const char* description = NULL)
+ : stat_t(name, description)
+ {}
+
+ /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); }
+
+};
+
+template<typename T, typename VALUE_T>
+void record(EventStatHandle<T>& measurement, VALUE_T value)
+{
+#if LL_TRACE_ENABLED
+ T converted_value(value);
+ measurement.getCurrentAccumulator().record(storage_value(converted_value));
+#endif
+}
+
+template <typename T = F64>
+class SampleStatHandle
+: public StatType<SampleAccumulator>
+{
+public:
+ typedef F64 storage_t;
+ typedef StatType<SampleAccumulator> stat_t;
+ typedef SampleStatHandle<T> self_t;
+
+ SampleStatHandle(const char* name, const char* description = NULL)
+ : stat_t(name, description)
+ {}
+
+ /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); }
+};
+
+template<typename T, typename VALUE_T>
+void sample(SampleStatHandle<T>& measurement, VALUE_T value)
+{
+#if LL_TRACE_ENABLED
+ T converted_value(value);
+ measurement.getCurrentAccumulator().sample(storage_value(converted_value));
+#endif
+}
+
+template <typename T = F64>
+class CountStatHandle
+: public StatType<CountAccumulator>
+{
+public:
+ typedef F64 storage_t;
+ typedef StatType<CountAccumulator> stat_t;
+ typedef CountStatHandle<T> self_t;
+
+ CountStatHandle(const char* name, const char* description = NULL)
+ : stat_t(name, description)
+ {}
+
+ /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); }
+};
+
+template<typename T, typename VALUE_T>
+void add(CountStatHandle<T>& count, VALUE_T value)
+{
+#if LL_TRACE_ENABLED
+ T converted_value(value);
+ count.getCurrentAccumulator().add(storage_value(converted_value));
+#endif
+}
+
+template<>
+class StatType<MemAccumulator::AllocationFacet>
+: public StatType<MemAccumulator>
+{
+public:
+
+ StatType(const char* name, const char* description = "")
+ : StatType<MemAccumulator>(name, description)
+ {}
+};
+
+template<>
+class StatType<MemAccumulator::DeallocationFacet>
+: public StatType<MemAccumulator>
+{
+public:
+
+ StatType(const char* name, const char* description = "")
+ : StatType<MemAccumulator>(name, description)
+ {}
+};
+
+class MemStatHandle : public StatType<MemAccumulator>
+{
+public:
+ typedef StatType<MemAccumulator> stat_t;
+ MemStatHandle(const char* name, const char* description = "")
+ : stat_t(name, description)
+ {
+ mName = name;
+ }
+
+ void setName(const char* name)
+ {
+ mName = name;
+ setKey(name);
+ }
+
+ /*virtual*/ const char* getUnitLabel() const { return "KB"; }
+
+ StatType<MemAccumulator::AllocationFacet>& allocations()
+ {
+ return static_cast<StatType<MemAccumulator::AllocationFacet>&>(*(StatType<MemAccumulator>*)this);
+ }
+
+ StatType<MemAccumulator::DeallocationFacet>& deallocations()
+ {
+ return static_cast<StatType<MemAccumulator::DeallocationFacet>&>(*(StatType<MemAccumulator>*)this);
+ }
+};
+
+
+// measures effective memory footprint of specified type
+// specialize to cover different types
+template<typename T, typename IS_MEM_TRACKABLE = void, typename IS_UNITS = void>
+struct MeasureMem
+{
+ static size_t measureFootprint(const T& value)
+ {
+ return sizeof(T);
+ }
+};
+
+template<typename T, typename IS_BYTES>
+struct MeasureMem<T, typename T::mem_trackable_tag_t, IS_BYTES>
+{
+ static size_t measureFootprint(const T& value)
+ {
+ return sizeof(T) + value.getMemFootprint();
+ }
+};
+
+template<typename T, typename IS_MEM_TRACKABLE>
+struct MeasureMem<T, IS_MEM_TRACKABLE, typename T::is_unit_t>
+{
+ static size_t measureFootprint(const T& value)
+ {
+ return U32Bytes(value).value();
+ }
+};
+
+template<typename T, typename IS_MEM_TRACKABLE, typename IS_BYTES>
+struct MeasureMem<T*, IS_MEM_TRACKABLE, IS_BYTES>
+{
+ static size_t measureFootprint(const T* value)
+ {
+ if (!value)
+ {
+ return 0;
+ }
+ return MeasureMem<T>::measureFootprint(*value);
+ }
+};
+
+template<typename T, typename IS_MEM_TRACKABLE, typename IS_BYTES>
+struct MeasureMem<LLPointer<T>, IS_MEM_TRACKABLE, IS_BYTES>
+{
+ static size_t measureFootprint(const LLPointer<T> value)
+ {
+ if (value.isNull())
+ {
+ return 0;
+ }
+ return MeasureMem<T>::measureFootprint(*value);
+ }
+};
+
+template<typename IS_MEM_TRACKABLE, typename IS_BYTES>
+struct MeasureMem<S32, IS_MEM_TRACKABLE, IS_BYTES>
+{
+ static size_t measureFootprint(S32 value)
+ {
+ return value;
+ }
+};
+
+template<typename IS_MEM_TRACKABLE, typename IS_BYTES>
+struct MeasureMem<U32, IS_MEM_TRACKABLE, IS_BYTES>
+{
+ static size_t measureFootprint(U32 value)
+ {
+ return value;
+ }
+};
+
+template<typename T, typename IS_MEM_TRACKABLE, typename IS_BYTES>
+struct MeasureMem<std::basic_string<T>, IS_MEM_TRACKABLE, IS_BYTES>
+{
+ static size_t measureFootprint(const std::basic_string<T>& value)
+ {
+ return value.capacity() * sizeof(T);
+ }
+};
+
+
+template<typename T>
+inline void claim_alloc(MemStatHandle& measurement, const T& value)
+{
+#if LL_TRACE_ENABLED
+ S32 size = MeasureMem<T>::measureFootprint(value);
+ if(size == 0) return;
+ MemAccumulator& accumulator = measurement.getCurrentAccumulator();
+ accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() + (F64)size : (F64)size);
+ accumulator.mAllocations.record(size);
+#endif
+}
+
+template<typename T>
+inline void disclaim_alloc(MemStatHandle& measurement, const T& value)
+{
+#if LL_TRACE_ENABLED
+ S32 size = MeasureMem<T>::measureFootprint(value);
+ if(size == 0) return;
+ MemAccumulator& accumulator = measurement.getCurrentAccumulator();
+ accumulator.mSize.sample(accumulator.mSize.hasValue() ? accumulator.mSize.getLastValue() - (F64)size : -(F64)size);
+ accumulator.mDeallocations.add(size);
+#endif
+}
+
+template<typename DERIVED, size_t ALIGNMENT = LL_DEFAULT_HEAP_ALIGN>
+class MemTrackableNonVirtual
+{
+public:
+ typedef void mem_trackable_tag_t;
+
+ MemTrackableNonVirtual(const char* name)
+#if LL_TRACE_ENABLED
+ : mMemFootprint(0)
+#endif
+ {
+#if LL_TRACE_ENABLED
+ static bool name_initialized = false;
+ if (!name_initialized)
+ {
+ name_initialized = true;
+ sMemStat.setName(name);
+ }
+#endif
+ }
+
+#if LL_TRACE_ENABLED
+ ~MemTrackableNonVirtual()
+ {
+ disclaimMem(mMemFootprint);
+ }
+
+ static MemStatHandle& getMemStatHandle()
+ {
+ return sMemStat;
+ }
+
+ S32 getMemFootprint() const { return mMemFootprint; }
+#endif
+
+ void* operator new(size_t size)
+ {
+#if LL_TRACE_ENABLED
+ claim_alloc(sMemStat, size);
+#endif
+ return ll_aligned_malloc<ALIGNMENT>(size);
+ }
+
+ template<int CUSTOM_ALIGNMENT>
+ static void* aligned_new(size_t size)
+ {
+#if LL_TRACE_ENABLED
+ claim_alloc(sMemStat, size);
+#endif
+ return ll_aligned_malloc<CUSTOM_ALIGNMENT>(size);
+ }
+
+ void operator delete(void* ptr, size_t size)
+ {
+#if LL_TRACE_ENABLED
+ disclaim_alloc(sMemStat, size);
+#endif
+ ll_aligned_free<ALIGNMENT>(ptr);
+ }
+
+ template<int CUSTOM_ALIGNMENT>
+ static void aligned_delete(void* ptr, size_t size)
+ {
+#if LL_TRACE_ENABLED
+ disclaim_alloc(sMemStat, size);
+#endif
+ ll_aligned_free<CUSTOM_ALIGNMENT>(ptr);
+ }
+
+ void* operator new [](size_t size)
+ {
+#if LL_TRACE_ENABLED
+ claim_alloc(sMemStat, size);
+#endif
+ return ll_aligned_malloc<ALIGNMENT>(size);
+ }
+
+ void operator delete[](void* ptr, size_t size)
+ {
+#if LL_TRACE_ENABLED
+ disclaim_alloc(sMemStat, size);
+#endif
+ ll_aligned_free<ALIGNMENT>(ptr);
+ }
+
+ // claim memory associated with other objects/data as our own, adding to our calculated footprint
+ template<typename CLAIM_T>
+ void claimMem(const CLAIM_T& value) const
+ {
+#if LL_TRACE_ENABLED
+ S32 size = MeasureMem<CLAIM_T>::measureFootprint(value);
+ claim_alloc(sMemStat, size);
+ mMemFootprint += size;
+#endif
+ }
+
+ // remove memory we had claimed from our calculated footprint
+ template<typename CLAIM_T>
+ void disclaimMem(const CLAIM_T& value) const
+ {
+#if LL_TRACE_ENABLED
+ S32 size = MeasureMem<CLAIM_T>::measureFootprint(value);
+ disclaim_alloc(sMemStat, size);
+ mMemFootprint -= size;
+#endif
+ }
+
+private:
+#if LL_TRACE_ENABLED
+ // use signed values so that we can temporarily go negative
+ // and reconcile in destructor
+ // NB: this assumes that no single class is responsible for > 2GB of allocations
+ mutable S32 mMemFootprint;
+
+ static MemStatHandle sMemStat;
+#endif
+
+};
+
+#if LL_TRACE_ENABLED
+template<typename DERIVED, size_t ALIGNMENT>
+MemStatHandle MemTrackableNonVirtual<DERIVED, ALIGNMENT>::sMemStat(typeid(MemTrackableNonVirtual<DERIVED, ALIGNMENT>).name());
+#endif
+
+template<typename DERIVED, size_t ALIGNMENT = LL_DEFAULT_HEAP_ALIGN>
+class MemTrackable : public MemTrackableNonVirtual<DERIVED, ALIGNMENT>
+{
+public:
+ MemTrackable(const char* name)
+ : MemTrackableNonVirtual<DERIVED, ALIGNMENT>(name)
+ {}
+
+ virtual ~MemTrackable()
+ {}
+};
+}
+
+#endif // LL_LLTRACE_H
diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp
new file mode 100644
index 0000000000..385d31edd7
--- /dev/null
+++ b/indra/llcommon/lltraceaccumulators.cpp
@@ -0,0 +1,302 @@
+/**
+ * @file lltracesampler.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltraceaccumulators.h"
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
+
+namespace LLTrace
+{
+
+extern MemStatHandle gTraceMemStat;
+
+
+///////////////////////////////////////////////////////////////////////
+// AccumulatorBufferGroup
+///////////////////////////////////////////////////////////////////////
+
+AccumulatorBufferGroup::AccumulatorBufferGroup()
+{
+ claim_alloc(gTraceMemStat, mCounts.capacity() * sizeof(CountAccumulator));
+ claim_alloc(gTraceMemStat, mSamples.capacity() * sizeof(SampleAccumulator));
+ claim_alloc(gTraceMemStat, mEvents.capacity() * sizeof(EventAccumulator));
+ claim_alloc(gTraceMemStat, mStackTimers.capacity() * sizeof(TimeBlockAccumulator));
+ claim_alloc(gTraceMemStat, mMemStats.capacity() * sizeof(MemAccumulator));
+}
+
+AccumulatorBufferGroup::AccumulatorBufferGroup(const AccumulatorBufferGroup& other)
+: mCounts(other.mCounts),
+ mSamples(other.mSamples),
+ mEvents(other.mEvents),
+ mStackTimers(other.mStackTimers),
+ mMemStats(other.mMemStats)
+{
+ claim_alloc(gTraceMemStat, mCounts.capacity() * sizeof(CountAccumulator));
+ claim_alloc(gTraceMemStat, mSamples.capacity() * sizeof(SampleAccumulator));
+ claim_alloc(gTraceMemStat, mEvents.capacity() * sizeof(EventAccumulator));
+ claim_alloc(gTraceMemStat, mStackTimers.capacity() * sizeof(TimeBlockAccumulator));
+ claim_alloc(gTraceMemStat, mMemStats.capacity() * sizeof(MemAccumulator));
+}
+
+AccumulatorBufferGroup::~AccumulatorBufferGroup()
+{
+ disclaim_alloc(gTraceMemStat, mCounts.capacity() * sizeof(CountAccumulator));
+ disclaim_alloc(gTraceMemStat, mSamples.capacity() * sizeof(SampleAccumulator));
+ disclaim_alloc(gTraceMemStat, mEvents.capacity() * sizeof(EventAccumulator));
+ disclaim_alloc(gTraceMemStat, mStackTimers.capacity() * sizeof(TimeBlockAccumulator));
+ disclaim_alloc(gTraceMemStat, mMemStats.capacity() * sizeof(MemAccumulator));
+}
+
+void AccumulatorBufferGroup::handOffTo(AccumulatorBufferGroup& other)
+{
+ other.mCounts.reset(&mCounts);
+ other.mSamples.reset(&mSamples);
+ other.mEvents.reset(&mEvents);
+ other.mStackTimers.reset(&mStackTimers);
+ other.mMemStats.reset(&mMemStats);
+}
+
+void AccumulatorBufferGroup::makeCurrent()
+{
+ mCounts.makeCurrent();
+ mSamples.makeCurrent();
+ mEvents.makeCurrent();
+ mStackTimers.makeCurrent();
+ mMemStats.makeCurrent();
+
+ ThreadRecorder* thread_recorder = get_thread_recorder().get();
+ AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers;
+ // update stacktimer parent pointers
+ for (S32 i = 0, end_i = mStackTimers.size(); i < end_i; i++)
+ {
+ TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(i);
+ if (tree_node)
+ {
+ timer_accumulator_buffer[i].mParent = tree_node->mParent;
+ }
+ }
+}
+
+//static
+void AccumulatorBufferGroup::clearCurrent()
+{
+ AccumulatorBuffer<CountAccumulator>::clearCurrent();
+ AccumulatorBuffer<SampleAccumulator>::clearCurrent();
+ AccumulatorBuffer<EventAccumulator>::clearCurrent();
+ AccumulatorBuffer<TimeBlockAccumulator>::clearCurrent();
+ AccumulatorBuffer<MemAccumulator>::clearCurrent();
+}
+
+bool AccumulatorBufferGroup::isCurrent() const
+{
+ return mCounts.isCurrent();
+}
+
+void AccumulatorBufferGroup::append( const AccumulatorBufferGroup& other )
+{
+ mCounts.addSamples(other.mCounts, SEQUENTIAL);
+ mSamples.addSamples(other.mSamples, SEQUENTIAL);
+ mEvents.addSamples(other.mEvents, SEQUENTIAL);
+ mMemStats.addSamples(other.mMemStats, SEQUENTIAL);
+ mStackTimers.addSamples(other.mStackTimers, SEQUENTIAL);
+}
+
+void AccumulatorBufferGroup::merge( const AccumulatorBufferGroup& other)
+{
+ mCounts.addSamples(other.mCounts, NON_SEQUENTIAL);
+ mSamples.addSamples(other.mSamples, NON_SEQUENTIAL);
+ mEvents.addSamples(other.mEvents, NON_SEQUENTIAL);
+ mMemStats.addSamples(other.mMemStats, NON_SEQUENTIAL);
+ // for now, hold out timers from merge, need to be displayed per thread
+ //mStackTimers.addSamples(other.mStackTimers, NON_SEQUENTIAL);
+}
+
+void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other)
+{
+ mCounts.reset(other ? &other->mCounts : NULL);
+ mSamples.reset(other ? &other->mSamples : NULL);
+ mEvents.reset(other ? &other->mEvents : NULL);
+ mStackTimers.reset(other ? &other->mStackTimers : NULL);
+ mMemStats.reset(other ? &other->mMemStats : NULL);
+}
+
+void AccumulatorBufferGroup::sync()
+{
+ if (isCurrent())
+ {
+ F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds();
+
+ mSamples.sync(time_stamp);
+ mMemStats.sync(time_stamp);
+ }
+}
+
+F64 SampleAccumulator::mergeSumsOfSquares(const SampleAccumulator& a, const SampleAccumulator& b)
+{
+ const F64 epsilon = 0.0000001;
+
+ if (a.getSamplingTime() > epsilon && b.getSamplingTime() > epsilon)
+ {
+ // combine variance (and hence standard deviation) of 2 different sized sample groups using
+ // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
+ F64 n_1 = a.getSamplingTime(),
+ n_2 = b.getSamplingTime();
+ F64 m_1 = a.getMean(),
+ m_2 = b.getMean();
+ F64 v_1 = a.getSumOfSquares() / a.getSamplingTime(),
+ v_2 = b.getSumOfSquares() / b.getSamplingTime();
+ if (n_1 < epsilon)
+ {
+ return b.getSumOfSquares();
+ }
+ else
+ {
+ return a.getSamplingTime()
+ * ((((n_1 - epsilon) * v_1)
+ + ((n_2 - epsilon) * v_2)
+ + (((n_1 * n_2) / (n_1 + n_2))
+ * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
+ / (n_1 + n_2 - epsilon));
+ }
+ }
+
+ return a.getSumOfSquares();
+}
+
+
+void SampleAccumulator::addSamples( const SampleAccumulator& other, EBufferAppendType append_type )
+{
+ if (append_type == NON_SEQUENTIAL)
+ {
+ return;
+ }
+
+ if (!mHasValue)
+ {
+ *this = other;
+
+ if (append_type == NON_SEQUENTIAL)
+ {
+ // restore own last value state
+ mLastValue = NaN;
+ mHasValue = false;
+ }
+ }
+ else if (other.mHasValue)
+ {
+ mSum += other.mSum;
+
+ if (other.mMin < mMin) { mMin = other.mMin; }
+ if (other.mMax > mMax) { mMax = other.mMax; }
+
+ mSumOfSquares = mergeSumsOfSquares(*this, other);
+
+ if (append_type == SEQUENTIAL)
+ {
+ mLastValue = other.mLastValue;
+ mLastSampleTimeStamp = other.mLastSampleTimeStamp;
+ }
+ }
+}
+
+void SampleAccumulator::reset( const SampleAccumulator* other )
+{
+ mLastValue = other ? other->mLastValue : NaN;
+ mHasValue = other ? other->mHasValue : false;
+ mNumSamples = 0;
+ mSum = 0;
+ mMin = mLastValue;
+ mMax = mLastValue;
+ mMean = mLastValue;
+ llassert(!mHasValue || mMean < 0 || mMean >= 0);
+ mSumOfSquares = 0;
+ mLastSampleTimeStamp = LLTimer::getTotalSeconds();
+ mTotalSamplingTime = 0;
+}
+
+F64 EventAccumulator::mergeSumsOfSquares(const EventAccumulator& a, const EventAccumulator& b)
+{
+ if (a.mNumSamples && b.mNumSamples)
+ {
+ // combine variance (and hence standard deviation) of 2 different sized sample groups using
+ // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm
+ F64 n_1 = a.mNumSamples,
+ n_2 = b.mNumSamples;
+ F64 m_1 = a.mMean,
+ m_2 = b.mMean;
+ F64 v_1 = a.mSumOfSquares / a.mNumSamples,
+ v_2 = b.mSumOfSquares / b.mNumSamples;
+ return (F64)a.mNumSamples
+ * ((((n_1 - 1.f) * v_1)
+ + ((n_2 - 1.f) * v_2)
+ + (((n_1 * n_2) / (n_1 + n_2))
+ * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2))))
+ / (n_1 + n_2 - 1.f));
+ }
+
+ return a.mSumOfSquares;
+}
+
+void EventAccumulator::addSamples( const EventAccumulator& other, EBufferAppendType append_type )
+{
+ if (other.mNumSamples)
+ {
+ if (!mNumSamples)
+ {
+ *this = other;
+ }
+ else
+ {
+ mSum += other.mSum;
+
+ // NOTE: both conditions will hold first time through
+ if (other.mMin < mMin) { mMin = other.mMin; }
+ if (other.mMax > mMax) { mMax = other.mMax; }
+
+ mSumOfSquares = mergeSumsOfSquares(*this, other);
+
+ F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples);
+ mNumSamples += other.mNumSamples;
+ mMean = mMean * weight + other.mMean * (1.f - weight);
+ if (append_type == SEQUENTIAL) mLastValue = other.mLastValue;
+ }
+ }
+}
+
+void EventAccumulator::reset( const EventAccumulator* other )
+{
+ mNumSamples = 0;
+ mSum = 0;
+ mMin = NaN;
+ mMax = NaN;
+ mMean = NaN;
+ mSumOfSquares = 0;
+ mLastValue = other ? other->mLastValue : NaN;
+}
+
+
+}
diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h
new file mode 100644
index 0000000000..6f27b97dff
--- /dev/null
+++ b/indra/llcommon/lltraceaccumulators.h
@@ -0,0 +1,602 @@
+
+/**
+ * @file lltraceaccumulators.h
+ * @brief Storage for accumulating statistics
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTRACEACCUMULATORS_H
+#define LL_LLTRACEACCUMULATORS_H
+
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+#include "llunits.h"
+#include "lltimer.h"
+#include "llrefcount.h"
+#include "llthreadlocalstorage.h"
+#include "llmemory.h"
+#include <limits>
+
+namespace LLTrace
+{
+ const F64 NaN = std::numeric_limits<double>::quiet_NaN();
+
+ enum EBufferAppendType
+ {
+ SEQUENTIAL,
+ NON_SEQUENTIAL
+ };
+
+ template<typename ACCUMULATOR>
+ class AccumulatorBuffer : public LLRefCount
+ {
+ typedef AccumulatorBuffer<ACCUMULATOR> self_t;
+ static const S32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 32;
+ private:
+ struct StaticAllocationMarker { };
+
+ AccumulatorBuffer(StaticAllocationMarker m)
+ : mStorageSize(0),
+ mStorage(NULL)
+ {}
+
+ public:
+ AccumulatorBuffer()
+ : mStorageSize(0),
+ mStorage(NULL)
+ {
+ const AccumulatorBuffer& other = *getDefaultBuffer();
+ resize(sNextStorageSlot);
+ for (S32 i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i] = other.mStorage[i];
+ }
+ }
+
+ ~AccumulatorBuffer()
+ {
+ if (isCurrent())
+ {
+ LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
+ }
+ delete[] mStorage;
+ }
+
+ LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index)
+ {
+ return mStorage[index];
+ }
+
+ LL_FORCE_INLINE const ACCUMULATOR& operator[](size_t index) const
+ {
+ return mStorage[index];
+ }
+
+
+ AccumulatorBuffer(const AccumulatorBuffer& other)
+ : mStorageSize(0),
+ mStorage(NULL)
+ {
+ resize(sNextStorageSlot);
+ for (S32 i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i] = other.mStorage[i];
+ }
+ }
+
+ void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, EBufferAppendType append_type)
+ {
+ llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i].addSamples(other.mStorage[i], append_type);
+ }
+ }
+
+ void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other)
+ {
+ llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i] = other.mStorage[i];
+ }
+ }
+
+ void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL)
+ {
+ llassert(mStorageSize >= sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i].reset(other ? &other->mStorage[i] : NULL);
+ }
+ }
+
+ void sync(F64SecondsImplicit time_stamp)
+ {
+ llassert(mStorageSize >= sNextStorageSlot);
+ for (size_t i = 0; i < sNextStorageSlot; i++)
+ {
+ mStorage[i].sync(time_stamp);
+ }
+ }
+
+ void makeCurrent()
+ {
+ LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(mStorage);
+ }
+
+ bool isCurrent() const
+ {
+ return LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance() == mStorage;
+ }
+
+ static void clearCurrent()
+ {
+ LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL);
+ }
+
+ // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned
+ size_t reserveSlot()
+ {
+ size_t next_slot = sNextStorageSlot++;
+ if (next_slot >= mStorageSize)
+ {
+ // don't perform doubling, as this should only happen during startup
+ // want to keep a tight bounds as we will have a lot of these buffers
+ resize(mStorageSize + mStorageSize / 2);
+ }
+ llassert(mStorage && next_slot < mStorageSize);
+ return next_slot;
+ }
+
+ void resize(size_t new_size)
+ {
+ if (new_size <= mStorageSize) return;
+
+ ACCUMULATOR* old_storage = mStorage;
+ mStorage = new ACCUMULATOR[new_size];
+ if (old_storage)
+ {
+ for (S32 i = 0; i < mStorageSize; i++)
+ {
+ mStorage[i] = old_storage[i];
+ }
+ }
+ mStorageSize = new_size;
+ delete[] old_storage;
+
+ self_t* default_buffer = getDefaultBuffer();
+ if (this != default_buffer
+ && new_size > default_buffer->size())
+ {
+ //NB: this is not thread safe, but we assume that all resizing occurs during static initialization
+ default_buffer->resize(new_size);
+ }
+ }
+
+ size_t size() const
+ {
+ return getNumIndices();
+ }
+
+ size_t capacity() const
+ {
+ return mStorageSize;
+ }
+
+ static size_t getNumIndices()
+ {
+ return sNextStorageSlot;
+ }
+
+ static self_t* getDefaultBuffer()
+ {
+ static bool sInitialized = false;
+ if (!sInitialized)
+ {
+ // this buffer is allowed to leak so that trace calls from global destructors have somewhere to put their data
+ // so as not to trigger an access violation
+ sDefaultBuffer = new AccumulatorBuffer(StaticAllocationMarker());
+ sInitialized = true;
+ sDefaultBuffer->resize(DEFAULT_ACCUMULATOR_BUFFER_SIZE);
+ }
+ return sDefaultBuffer;
+ }
+
+ private:
+ ACCUMULATOR* mStorage;
+ size_t mStorageSize;
+ static size_t sNextStorageSlot;
+ static self_t* sDefaultBuffer;
+ };
+
+ template<typename ACCUMULATOR> size_t AccumulatorBuffer<ACCUMULATOR>::sNextStorageSlot = 0;
+ template<typename ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>* AccumulatorBuffer<ACCUMULATOR>::sDefaultBuffer = NULL;
+
+ class EventAccumulator
+ {
+ public:
+ typedef F64 value_t;
+ static F64 getDefaultValue() { return NaN; }
+
+ EventAccumulator()
+ : mSum(0),
+ mMin(NaN),
+ mMax(NaN),
+ mMean(NaN),
+ mSumOfSquares(0),
+ mNumSamples(0),
+ mLastValue(NaN)
+ {}
+
+ void record(F64 value)
+ {
+ if (mNumSamples == 0)
+ {
+ mSum = value;
+ mMean = value;
+ mMin = value;
+ mMax = value;
+ }
+ else
+ {
+ mSum += value;
+ F64 old_mean = mMean;
+ mMean += (value - old_mean) / (F64)mNumSamples;
+ mSumOfSquares += (value - old_mean) * (value - mMean);
+
+ if (value < mMin) { mMin = value; }
+ else if (value > mMax) { mMax = value; }
+ }
+
+ mNumSamples++;
+ mLastValue = value;
+ }
+
+ void addSamples(const EventAccumulator& other, EBufferAppendType append_type);
+ void reset(const EventAccumulator* other);
+ void sync(F64SecondsImplicit) {}
+
+ F64 getSum() const { return mSum; }
+ F32 getMin() const { return mMin; }
+ F32 getMax() const { return mMax; }
+ F64 getLastValue() const { return mLastValue; }
+ F64 getMean() const { return mMean; }
+ F64 getStandardDeviation() const { return sqrtf(mSumOfSquares / mNumSamples); }
+ F64 getSumOfSquares() const { return mSumOfSquares; }
+ S32 getSampleCount() const { return mNumSamples; }
+ bool hasValue() const { return mNumSamples > 0; }
+
+ // helper utility to calculate combined sumofsquares total
+ static F64 mergeSumsOfSquares(const EventAccumulator& a, const EventAccumulator& b);
+
+ private:
+ F64 mSum,
+ mLastValue;
+
+ F64 mMean,
+ mSumOfSquares;
+
+ F32 mMin,
+ mMax;
+
+ S32 mNumSamples;
+ };
+
+
+ class SampleAccumulator
+ {
+ public:
+ typedef F64 value_t;
+ static F64 getDefaultValue() { return NaN; }
+
+ SampleAccumulator()
+ : mSum(0),
+ mMin(NaN),
+ mMax(NaN),
+ mMean(NaN),
+ mSumOfSquares(0),
+ mLastSampleTimeStamp(0),
+ mTotalSamplingTime(0),
+ mNumSamples(0),
+ mLastValue(NaN),
+ mHasValue(false)
+ {}
+
+ void sample(F64 value)
+ {
+ F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds();
+
+ // store effect of last value
+ sync(time_stamp);
+
+ if (!mHasValue)
+ {
+ mHasValue = true;
+
+ mMin = value;
+ mMax = value;
+ mMean = value;
+ mLastSampleTimeStamp = time_stamp;
+ }
+ else
+ {
+ if (value < mMin) { mMin = value; }
+ else if (value > mMax) { mMax = value; }
+ }
+
+ mLastValue = value;
+ mNumSamples++;
+ }
+
+ void addSamples(const SampleAccumulator& other, EBufferAppendType append_type);
+ void reset(const SampleAccumulator* other);
+
+ void sync(F64SecondsImplicit time_stamp)
+ {
+ if (mHasValue && time_stamp != mLastSampleTimeStamp)
+ {
+ F64SecondsImplicit delta_time = time_stamp - mLastSampleTimeStamp;
+ mSum += mLastValue * delta_time;
+ mTotalSamplingTime += delta_time;
+ F64 old_mean = mMean;
+ mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean);
+ mSumOfSquares += delta_time * (mLastValue - old_mean) * (mLastValue - mMean);
+ }
+ mLastSampleTimeStamp = time_stamp;
+ }
+
+ F64 getSum() const { return mSum; }
+ F32 getMin() const { return mMin; }
+ F32 getMax() const { return mMax; }
+ F64 getLastValue() const { return mLastValue; }
+ F64 getMean() const { return mMean; }
+ F64 getStandardDeviation() const { return sqrtf(mSumOfSquares / mTotalSamplingTime); }
+ F64 getSumOfSquares() const { return mSumOfSquares; }
+ F64SecondsImplicit getSamplingTime() const { return mTotalSamplingTime; }
+ S32 getSampleCount() const { return mNumSamples; }
+ bool hasValue() const { return mHasValue; }
+
+ // helper utility to calculate combined sumofsquares total
+ static F64 mergeSumsOfSquares(const SampleAccumulator& a, const SampleAccumulator& b);
+
+ private:
+ F64 mSum,
+ mLastValue;
+
+ F64 mMean,
+ mSumOfSquares;
+
+ F64SecondsImplicit
+ mLastSampleTimeStamp,
+ mTotalSamplingTime;
+
+ F32 mMin,
+ mMax;
+
+ S32 mNumSamples;
+ // distinct from mNumSamples, since we might have inherited a last value from
+ // a previous sampling period
+ bool mHasValue;
+ };
+
+ class CountAccumulator
+ {
+ public:
+ typedef F64 value_t;
+ static F64 getDefaultValue() { return 0; }
+
+ CountAccumulator()
+ : mSum(0),
+ mNumSamples(0)
+ {}
+
+ void add(F64 value)
+ {
+ mNumSamples++;
+ mSum += value;
+ }
+
+ void addSamples(const CountAccumulator& other, EBufferAppendType /*type*/)
+ {
+ mSum += other.mSum;
+ mNumSamples += other.mNumSamples;
+ }
+
+ void reset(const CountAccumulator* other)
+ {
+ mNumSamples = 0;
+ mSum = 0;
+ }
+
+ void sync(F64SecondsImplicit) {}
+
+ F64 getSum() const { return mSum; }
+
+ S32 getSampleCount() const { return mNumSamples; }
+
+ bool hasValue() const { return true; }
+
+ private:
+ F64 mSum;
+
+ S32 mNumSamples;
+ };
+
+ class TimeBlockAccumulator
+ {
+ public:
+ typedef F64Seconds value_t;
+ static F64Seconds getDefaultValue() { return F64Seconds(0); }
+
+ typedef TimeBlockAccumulator self_t;
+
+ // fake classes that allows us to view different facets of underlying statistic
+ struct CallCountFacet
+ {
+ typedef S32 value_t;
+ };
+
+ struct SelfTimeFacet
+ {
+ typedef F64Seconds value_t;
+ };
+
+ // arrays are allocated with 32 byte alignment
+ void *operator new [](size_t size)
+ {
+ return ll_aligned_malloc<32>(size);
+ }
+
+ void operator delete[](void* ptr, size_t size)
+ {
+ ll_aligned_free<32>(ptr);
+ }
+
+ TimeBlockAccumulator();
+ void addSamples(const self_t& other, EBufferAppendType append_type);
+ void reset(const self_t* other);
+ void sync(F64SecondsImplicit) {}
+ bool hasValue() const { return true; }
+
+ //
+ // members
+ //
+ U64 mTotalTimeCounter,
+ mSelfTimeCounter;
+ S32 mCalls;
+ class BlockTimerStatHandle* mParent; // last acknowledged parent of this time block
+ class BlockTimerStatHandle* mLastCaller; // used to bootstrap tree construction
+ U16 mActiveCount; // number of timers with this ID active on stack
+ bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
+
+ };
+
+ class BlockTimerStatHandle;
+
+ class TimeBlockTreeNode
+ {
+ public:
+ TimeBlockTreeNode();
+
+ void setParent(BlockTimerStatHandle* parent);
+ BlockTimerStatHandle* getParent() { return mParent; }
+
+ BlockTimerStatHandle* mBlock;
+ BlockTimerStatHandle* mParent;
+ std::vector<BlockTimerStatHandle*> mChildren;
+ bool mCollapsed;
+ bool mNeedsSorting;
+ };
+
+ struct BlockTimerStackRecord
+ {
+ class BlockTimer* mActiveTimer;
+ class BlockTimerStatHandle* mTimeBlock;
+ U64 mChildTime;
+ };
+
+ struct MemAccumulator
+ {
+ typedef F64Bytes value_t;
+ static F64Bytes getDefaultValue() { return F64Bytes(0); }
+
+ typedef MemAccumulator self_t;
+
+ // fake classes that allows us to view different facets of underlying statistic
+ struct AllocationFacet
+ {
+ typedef F64Bytes value_t;
+ static F64Bytes getDefaultValue() { return F64Bytes(0); }
+ };
+
+ struct DeallocationFacet
+ {
+ typedef F64Bytes value_t;
+ static F64Bytes getDefaultValue() { return F64Bytes(0); }
+ };
+
+ void addSamples(const MemAccumulator& other, EBufferAppendType append_type)
+ {
+ mAllocations.addSamples(other.mAllocations, append_type);
+ mDeallocations.addSamples(other.mDeallocations, append_type);
+
+ if (append_type == SEQUENTIAL)
+ {
+ mSize.addSamples(other.mSize, SEQUENTIAL);
+ }
+ else
+ {
+ F64 allocation_delta(other.mAllocations.getSum() - other.mDeallocations.getSum());
+ mSize.sample(mSize.hasValue()
+ ? mSize.getLastValue() + allocation_delta
+ : allocation_delta);
+ }
+ }
+
+ void reset(const MemAccumulator* other)
+ {
+ mSize.reset(other ? &other->mSize : NULL);
+ mAllocations.reset(other ? &other->mAllocations : NULL);
+ mDeallocations.reset(other ? &other->mDeallocations : NULL);
+ }
+
+ void sync(F64SecondsImplicit time_stamp)
+ {
+ mSize.sync(time_stamp);
+ }
+
+ bool hasValue() const { return mSize.hasValue(); }
+
+ SampleAccumulator mSize;
+ EventAccumulator mAllocations;
+ CountAccumulator mDeallocations;
+ };
+
+ struct AccumulatorBufferGroup : public LLRefCount
+ {
+ AccumulatorBufferGroup();
+ AccumulatorBufferGroup(const AccumulatorBufferGroup&);
+ ~AccumulatorBufferGroup();
+
+ void handOffTo(AccumulatorBufferGroup& other);
+ void makeCurrent();
+ bool isCurrent() const;
+ static void clearCurrent();
+
+ void append(const AccumulatorBufferGroup& other);
+ void merge(const AccumulatorBufferGroup& other);
+ void reset(AccumulatorBufferGroup* other = NULL);
+ void sync();
+
+ AccumulatorBuffer<CountAccumulator> mCounts;
+ AccumulatorBuffer<SampleAccumulator> mSamples;
+ AccumulatorBuffer<EventAccumulator> mEvents;
+ AccumulatorBuffer<TimeBlockAccumulator> mStackTimers;
+ AccumulatorBuffer<MemAccumulator> mMemStats;
+ };
+}
+
+#endif // LL_LLTRACEACCUMULATORS_H
+
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
new file mode 100644
index 0000000000..0b10438b9f
--- /dev/null
+++ b/indra/llcommon/lltracerecording.cpp
@@ -0,0 +1,1251 @@
+/**
+ * @file lltracesampler.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltracerecording.h"
+
+#include "lltrace.h"
+#include "llfasttimer.h"
+#include "lltracethreadrecorder.h"
+#include "llthread.h"
+
+inline F64 lerp(F64 a, F64 b, F64 u)
+{
+ return a + ((b - a) * u);
+}
+
+namespace LLTrace
+{
+
+extern MemStatHandle gTraceMemStat;
+
+///////////////////////////////////////////////////////////////////////
+// Recording
+///////////////////////////////////////////////////////////////////////
+
+Recording::Recording(EPlayState state)
+: mElapsedSeconds(0),
+ mActiveBuffers(NULL)
+{
+ claim_alloc(gTraceMemStat, this);
+ mBuffers = new AccumulatorBufferGroup();
+ claim_alloc(gTraceMemStat, mBuffers);
+ setPlayState(state);
+}
+
+Recording::Recording( const Recording& other )
+: mActiveBuffers(NULL)
+{
+ claim_alloc(gTraceMemStat, this);
+ *this = other;
+}
+
+Recording& Recording::operator = (const Recording& other)
+{
+ // this will allow us to seamlessly start without affecting any data we've acquired from other
+ setPlayState(PAUSED);
+
+ const_cast<Recording&>(other).update();
+ EPlayState other_play_state = other.getPlayState();
+
+ mBuffers = other.mBuffers;
+
+ // above call will clear mElapsedSeconds as a side effect, so copy it here
+ mElapsedSeconds = other.mElapsedSeconds;
+ mSamplingTimer = other.mSamplingTimer;
+
+ setPlayState(other_play_state);
+
+ return *this;
+}
+
+
+Recording::~Recording()
+{
+ disclaim_alloc(gTraceMemStat, this);
+ disclaim_alloc(gTraceMemStat, mBuffers);
+
+ // allow recording destruction without thread recorder running,
+ // otherwise thread shutdown could crash if a recording outlives the thread recorder
+ // besides, recording construction and destruction is fine without a recorder...just don't attempt to start one
+ if (isStarted() && LLTrace::get_thread_recorder().notNull())
+ {
+ LLTrace::get_thread_recorder()->deactivate(mBuffers.write());
+ }
+}
+
+// brings recording to front of recorder stack, with up to date info
+void Recording::update()
+{
+#if LL_TRACE_ENABLED
+ if (isStarted())
+ {
+ mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
+
+ // must have
+ llassert(mActiveBuffers != NULL
+ && LLTrace::get_thread_recorder().notNull());
+
+ if(!mActiveBuffers->isCurrent())
+ {
+ AccumulatorBufferGroup* buffers = mBuffers.write();
+ LLTrace::get_thread_recorder()->deactivate(buffers);
+ mActiveBuffers = LLTrace::get_thread_recorder()->activate(buffers);
+ }
+
+ mSamplingTimer.reset();
+ }
+#endif
+}
+
+void Recording::handleReset()
+{
+#if LL_TRACE_ENABLED
+ mBuffers.write()->reset();
+
+ mElapsedSeconds = F64Seconds(0.0);
+ mSamplingTimer.reset();
+#endif
+}
+
+void Recording::handleStart()
+{
+#if LL_TRACE_ENABLED
+ mSamplingTimer.reset();
+ mBuffers.setStayUnique(true);
+ // must have thread recorder running on this thread
+ llassert(LLTrace::get_thread_recorder().notNull());
+ mActiveBuffers = LLTrace::get_thread_recorder()->activate(mBuffers.write());
+#endif
+}
+
+void Recording::handleStop()
+{
+#if LL_TRACE_ENABLED
+ mElapsedSeconds += mSamplingTimer.getElapsedTimeF64();
+ // must have thread recorder running on this thread
+ llassert(LLTrace::get_thread_recorder().notNull());
+ LLTrace::get_thread_recorder()->deactivate(mBuffers.write());
+ mActiveBuffers = NULL;
+ mBuffers.setStayUnique(false);
+#endif
+}
+
+void Recording::handleSplitTo(Recording& other)
+{
+#if LL_TRACE_ENABLED
+ mBuffers.write()->handOffTo(*other.mBuffers.write());
+#endif
+}
+
+void Recording::appendRecording( Recording& other )
+{
+#if LL_TRACE_ENABLED
+ update();
+ other.update();
+ mBuffers.write()->append(*other.mBuffers);
+ mElapsedSeconds += other.mElapsedSeconds;
+#endif
+}
+
+bool Recording::hasValue(const StatType<TimeBlockAccumulator>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+ return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue());
+}
+
+F64Seconds Recording::getSum(const StatType<TimeBlockAccumulator>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+ return F64Seconds((F64)(accumulator.mTotalTimeCounter) + (F64)(active_accumulator ? active_accumulator->mTotalTimeCounter : 0))
+ / (F64)LLTrace::BlockTimer::countsPerSecond();
+}
+
+F64Seconds Recording::getSum(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+ return F64Seconds(((F64)(accumulator.mSelfTimeCounter) + (F64)(active_accumulator ? active_accumulator->mSelfTimeCounter : 0)) / (F64)LLTrace::BlockTimer::countsPerSecond());
+}
+
+
+S32 Recording::getSum(const StatType<TimeBlockAccumulator::CallCountFacet>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+ return accumulator.mCalls + (active_accumulator ? active_accumulator->mCalls : 0);
+}
+
+F64Seconds Recording::getPerSec(const StatType<TimeBlockAccumulator>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+
+ return F64Seconds((F64)(accumulator.mTotalTimeCounter + (active_accumulator ? active_accumulator->mTotalTimeCounter : 0))
+ / ((F64)LLTrace::BlockTimer::countsPerSecond() * mElapsedSeconds.value()));
+}
+
+F64Seconds Recording::getPerSec(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+
+ return F64Seconds((F64)(accumulator.mSelfTimeCounter + (active_accumulator ? active_accumulator->mSelfTimeCounter : 0))
+ / ((F64)LLTrace::BlockTimer::countsPerSecond() * mElapsedSeconds.value()));
+}
+
+F32 Recording::getPerSec(const StatType<TimeBlockAccumulator::CallCountFacet>& stat)
+{
+ update();
+ const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()];
+ const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL;
+ return (F32)(accumulator.mCalls + (active_accumulator ? active_accumulator->mCalls : 0)) / mElapsedSeconds.value();
+}
+
+bool Recording::hasValue(const StatType<MemAccumulator>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return accumulator.mSize.hasValue() || (active_accumulator && active_accumulator->mSize.hasValue() ? active_accumulator->mSize.hasValue() : false);
+}
+
+F64Kilobytes Recording::getMin(const StatType<MemAccumulator>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes(llmin(accumulator.mSize.getMin(), (active_accumulator && active_accumulator->mSize.hasValue() ? active_accumulator->mSize.getMin() : F32_MAX)));
+}
+
+F64Kilobytes Recording::getMean(const StatType<MemAccumulator>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+
+ if (active_accumulator && active_accumulator->mSize.hasValue())
+ {
+ return F64Bytes(lerp(accumulator.mSize.getMean(), active_accumulator->mSize.getMean(), active_accumulator->mSize.getSampleCount() / (accumulator.mSize.getSampleCount() + active_accumulator->mSize.getSampleCount())));
+ }
+ else
+ {
+ return F64Bytes(accumulator.mSize.getMean());
+ }
+}
+
+F64Kilobytes Recording::getMax(const StatType<MemAccumulator>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes(llmax(accumulator.mSize.getMax(), active_accumulator && active_accumulator->mSize.hasValue() ? active_accumulator->mSize.getMax() : F32_MIN));
+}
+
+F64Kilobytes Recording::getStandardDeviation(const StatType<MemAccumulator>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ if (active_accumulator && active_accumulator->hasValue())
+ {
+ F64 sum_of_squares = SampleAccumulator::mergeSumsOfSquares(accumulator.mSize, active_accumulator->mSize);
+ return F64Bytes(sqrtf(sum_of_squares / (accumulator.mSize.getSamplingTime().value() + active_accumulator->mSize.getSamplingTime().value())));
+ }
+ else
+ {
+ return F64Bytes(accumulator.mSize.getStandardDeviation());
+ }
+}
+
+F64Kilobytes Recording::getLastValue(const StatType<MemAccumulator>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes(active_accumulator ? active_accumulator->mSize.getLastValue() : accumulator.mSize.getLastValue());
+}
+
+bool Recording::hasValue(const StatType<MemAccumulator::AllocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return accumulator.mAllocations.hasValue() || (active_accumulator ? active_accumulator->mAllocations.hasValue() : false);
+}
+
+F64Kilobytes Recording::getSum(const StatType<MemAccumulator::AllocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes(accumulator.mAllocations.getSum() + (active_accumulator ? active_accumulator->mAllocations.getSum() : 0));
+}
+
+F64Kilobytes Recording::getPerSec(const StatType<MemAccumulator::AllocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes((accumulator.mAllocations.getSum() + (active_accumulator ? active_accumulator->mAllocations.getSum() : 0)) / mElapsedSeconds.value());
+}
+
+S32 Recording::getSampleCount(const StatType<MemAccumulator::AllocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return accumulator.mAllocations.getSampleCount() + (active_accumulator ? active_accumulator->mAllocations.getSampleCount() : 0);
+}
+
+bool Recording::hasValue(const StatType<MemAccumulator::DeallocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return accumulator.mDeallocations.hasValue() || (active_accumulator ? active_accumulator->mDeallocations.hasValue() : false);
+}
+
+
+F64Kilobytes Recording::getSum(const StatType<MemAccumulator::DeallocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes(accumulator.mDeallocations.getSum() + (active_accumulator ? active_accumulator->mDeallocations.getSum() : 0));
+}
+
+F64Kilobytes Recording::getPerSec(const StatType<MemAccumulator::DeallocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return F64Bytes((accumulator.mDeallocations.getSum() + (active_accumulator ? active_accumulator->mDeallocations.getSum() : 0)) / mElapsedSeconds.value());
+}
+
+S32 Recording::getSampleCount(const StatType<MemAccumulator::DeallocationFacet>& stat)
+{
+ update();
+ const MemAccumulator& accumulator = mBuffers->mMemStats[stat.getIndex()];
+ const MemAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mMemStats[stat.getIndex()] : NULL;
+ return accumulator.mDeallocations.getSampleCount() + (active_accumulator ? active_accumulator->mDeallocations.getSampleCount() : 0);
+}
+
+bool Recording::hasValue(const StatType<CountAccumulator>& stat)
+{
+ update();
+ const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
+ const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
+ return accumulator.hasValue() || (active_accumulator ? active_accumulator->hasValue() : false);
+}
+
+F64 Recording::getSum(const StatType<CountAccumulator>& stat)
+{
+ update();
+ const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
+ const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
+ return accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0);
+}
+
+F64 Recording::getPerSec( const StatType<CountAccumulator>& stat )
+{
+ update();
+ const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
+ const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
+ F64 sum = accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0);
+ return sum / mElapsedSeconds.value();
+}
+
+S32 Recording::getSampleCount( const StatType<CountAccumulator>& stat )
+{
+ update();
+ const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()];
+ const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL;
+ return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0);
+}
+
+bool Recording::hasValue(const StatType<SampleAccumulator>& stat)
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+ return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue());
+}
+
+F64 Recording::getMin( const StatType<SampleAccumulator>& stat )
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+ return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX);
+}
+
+F64 Recording::getMax( const StatType<SampleAccumulator>& stat )
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+ return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN);
+}
+
+F64 Recording::getMean( const StatType<SampleAccumulator>& stat )
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+ if (active_accumulator && active_accumulator->hasValue())
+ {
+ return lerp(accumulator.getMean(), active_accumulator->getMean(), active_accumulator->getSampleCount() / (accumulator.getSampleCount() + active_accumulator->getSampleCount()));
+ }
+ else
+ {
+ return accumulator.getMean();
+ }
+}
+
+F64 Recording::getStandardDeviation( const StatType<SampleAccumulator>& stat )
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+
+ if (active_accumulator && active_accumulator->hasValue())
+ {
+ F64 sum_of_squares = SampleAccumulator::mergeSumsOfSquares(accumulator, *active_accumulator);
+ return sqrtf(sum_of_squares / (accumulator.getSamplingTime() + active_accumulator->getSamplingTime()));
+ }
+ else
+ {
+ return accumulator.getStandardDeviation();
+ }
+}
+
+F64 Recording::getLastValue( const StatType<SampleAccumulator>& stat )
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+ return (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getLastValue() : accumulator.getLastValue());
+}
+
+S32 Recording::getSampleCount( const StatType<SampleAccumulator>& stat )
+{
+ update();
+ const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()];
+ const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL;
+ return accumulator.getSampleCount() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSampleCount() : 0);
+}
+
+bool Recording::hasValue(const StatType<EventAccumulator>& stat)
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue());
+}
+
+F64 Recording::getSum( const StatType<EventAccumulator>& stat)
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ return (F64)(accumulator.getSum() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSum() : 0));
+}
+
+F64 Recording::getMin( const StatType<EventAccumulator>& stat )
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX);
+}
+
+F64 Recording::getMax( const StatType<EventAccumulator>& stat )
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN);
+}
+
+F64 Recording::getMean( const StatType<EventAccumulator>& stat )
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ if (active_accumulator && active_accumulator->hasValue())
+ {
+ return lerp(accumulator.getMean(), active_accumulator->getMean(), active_accumulator->getSampleCount() / (accumulator.getSampleCount() + active_accumulator->getSampleCount()));
+ }
+ else
+ {
+ return accumulator.getMean();
+ }
+}
+
+F64 Recording::getStandardDeviation( const StatType<EventAccumulator>& stat )
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+
+ if (active_accumulator && active_accumulator->hasValue())
+ {
+ F64 sum_of_squares = EventAccumulator::mergeSumsOfSquares(accumulator, *active_accumulator);
+ return sqrtf(sum_of_squares / (accumulator.getSampleCount() + active_accumulator->getSampleCount()));
+ }
+ else
+ {
+ return accumulator.getStandardDeviation();
+ }
+}
+
+F64 Recording::getLastValue( const StatType<EventAccumulator>& stat )
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ return active_accumulator ? active_accumulator->getLastValue() : accumulator.getLastValue();
+}
+
+S32 Recording::getSampleCount( const StatType<EventAccumulator>& stat )
+{
+ update();
+ const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()];
+ const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL;
+ return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0);
+}
+
+///////////////////////////////////////////////////////////////////////
+// PeriodicRecording
+///////////////////////////////////////////////////////////////////////
+
+PeriodicRecording::PeriodicRecording( S32 num_periods, EPlayState state)
+: mAutoResize(num_periods == 0),
+ mCurPeriod(0),
+ mNumRecordedPeriods(0),
+ mRecordingPeriods(num_periods ? num_periods : 1)
+{
+ setPlayState(state);
+ claim_alloc(gTraceMemStat, this);
+}
+
+PeriodicRecording::~PeriodicRecording()
+{
+ disclaim_alloc(gTraceMemStat, this);
+}
+
+void PeriodicRecording::nextPeriod()
+{
+ if (mAutoResize)
+ {
+ mRecordingPeriods.push_back(Recording());
+ }
+
+ Recording& old_recording = getCurRecording();
+ mCurPeriod = (mCurPeriod + 1) % mRecordingPeriods.size();
+ old_recording.splitTo(getCurRecording());
+
+ mNumRecordedPeriods = llmin((S32)mRecordingPeriods.size() - 1, mNumRecordedPeriods + 1);
+}
+
+void PeriodicRecording::appendRecording(Recording& recording)
+{
+ getCurRecording().appendRecording(recording);
+ nextPeriod();
+}
+
+
+void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other )
+{
+ if (other.mRecordingPeriods.empty()) return;
+
+ getCurRecording().update();
+ other.getCurRecording().update();
+
+ const S32 other_recording_slots = other.mRecordingPeriods.size();
+ const S32 other_num_recordings = other.getNumRecordedPeriods();
+ const S32 other_current_recording_index = other.mCurPeriod;
+ const S32 other_oldest_recording_index = (other_current_recording_index + other_recording_slots - other_num_recordings) % other_recording_slots;
+
+ // append first recording into our current slot
+ getCurRecording().appendRecording(other.mRecordingPeriods[other_oldest_recording_index]);
+
+ // from now on, add new recordings for everything after the first
+ S32 other_index = (other_oldest_recording_index + 1) % other_recording_slots;
+
+ if (mAutoResize)
+ {
+ // push back recordings for everything in the middle
+ S32 other_index = (other_oldest_recording_index + 1) % other_recording_slots;
+ while (other_index != other_current_recording_index)
+ {
+ mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]);
+ other_index = (other_index + 1) % other_recording_slots;
+ }
+
+ // add final recording, if it wasn't already added as the first
+ if (other_num_recordings > 1)
+ {
+ mRecordingPeriods.push_back(other.mRecordingPeriods[other_current_recording_index]);
+ }
+
+ mCurPeriod = mRecordingPeriods.size() - 1;
+ mNumRecordedPeriods = mRecordingPeriods.size() - 1;
+ }
+ else
+ {
+ S32 num_to_copy = llmin((S32)mRecordingPeriods.size(), (S32)other_num_recordings);
+
+ std::vector<Recording>::iterator src_it = other.mRecordingPeriods.begin() + other_index ;
+ std::vector<Recording>::iterator dest_it = mRecordingPeriods.begin() + mCurPeriod;
+
+ // already consumed the first recording from other, so start counting at 1
+ for(S32 i = 1; i < num_to_copy; i++)
+ {
+ *dest_it = *src_it;
+
+ if (++src_it == other.mRecordingPeriods.end())
+ {
+ src_it = other.mRecordingPeriods.begin();
+ }
+
+ if (++dest_it == mRecordingPeriods.end())
+ {
+ dest_it = mRecordingPeriods.begin();
+ }
+ }
+
+ // want argument to % to be positive, otherwise result could be negative and thus out of bounds
+ llassert(num_to_copy >= 1);
+ // advance to last recording period copied, and make that our current period
+ mCurPeriod = (mCurPeriod + num_to_copy - 1) % mRecordingPeriods.size();
+ mNumRecordedPeriods = llmin((S32)mRecordingPeriods.size() - 1, mNumRecordedPeriods + num_to_copy - 1);
+ }
+
+ // end with fresh period, otherwise next appendPeriodicRecording() will merge the first
+ // recording period with the last one appended here
+ nextPeriod();
+ getCurRecording().setPlayState(getPlayState());
+}
+
+F64Seconds PeriodicRecording::getDuration() const
+{
+ F64Seconds duration;
+ S32 num_periods = mRecordingPeriods.size();
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ S32 index = (mCurPeriod + num_periods - i) % num_periods;
+ duration += mRecordingPeriods[index].getDuration();
+ }
+ return duration;
+}
+
+
+LLTrace::Recording PeriodicRecording::snapshotCurRecording() const
+{
+ Recording recording_copy(getCurRecording());
+ recording_copy.stop();
+ return recording_copy;
+}
+
+
+Recording& PeriodicRecording::getLastRecording()
+{
+ return getPrevRecording(1);
+}
+
+const Recording& PeriodicRecording::getLastRecording() const
+{
+ return getPrevRecording(1);
+}
+
+Recording& PeriodicRecording::getCurRecording()
+{
+ return mRecordingPeriods[mCurPeriod];
+}
+
+const Recording& PeriodicRecording::getCurRecording() const
+{
+ return mRecordingPeriods[mCurPeriod];
+}
+
+Recording& PeriodicRecording::getPrevRecording( S32 offset )
+{
+ S32 num_periods = mRecordingPeriods.size();
+ offset = llclamp(offset, 0, num_periods - 1);
+ return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
+}
+
+const Recording& PeriodicRecording::getPrevRecording( S32 offset ) const
+{
+ S32 num_periods = mRecordingPeriods.size();
+ offset = llclamp(offset, 0, num_periods - 1);
+ return mRecordingPeriods[(mCurPeriod + num_periods - offset) % num_periods];
+}
+
+void PeriodicRecording::handleStart()
+{
+ getCurRecording().start();
+}
+
+void PeriodicRecording::handleStop()
+{
+ getCurRecording().pause();
+}
+
+void PeriodicRecording::handleReset()
+{
+ getCurRecording().stop();
+
+ if (mAutoResize)
+ {
+ mRecordingPeriods.clear();
+ mRecordingPeriods.push_back(Recording());
+ }
+ else
+ {
+ for (std::vector<Recording>::iterator it = mRecordingPeriods.begin(), end_it = mRecordingPeriods.end();
+ it != end_it;
+ ++it)
+ {
+ it->reset();
+ }
+ }
+ mCurPeriod = 0;
+ mNumRecordedPeriods = 0;
+ getCurRecording().setPlayState(getPlayState());
+}
+
+void PeriodicRecording::handleSplitTo(PeriodicRecording& other)
+{
+ getCurRecording().splitTo(other.getCurRecording());
+}
+
+F64 PeriodicRecording::getPeriodMin( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ bool has_value = false;
+ F64 min_val = std::numeric_limits<F64>::max();
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ min_val = llmin(min_val, recording.getMin(stat));
+ has_value = true;
+ }
+ }
+
+ return has_value
+ ? min_val
+ : NaN;
+}
+
+F64 PeriodicRecording::getPeriodMax( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ bool has_value = false;
+ F64 max_val = std::numeric_limits<F64>::min();
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ max_val = llmax(max_val, recording.getMax(stat));
+ has_value = true;
+ }
+ }
+
+ return has_value
+ ? max_val
+ : NaN;
+}
+
+// calculates means using aggregates per period
+F64 PeriodicRecording::getPeriodMean( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64 mean = 0;
+ S32 valid_period_count = 0;
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ mean += recording.getMean(stat);
+ valid_period_count++;
+ }
+ }
+
+ return valid_period_count
+ ? mean / (F64)valid_period_count
+ : NaN;
+}
+
+
+F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<EventAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64 period_mean = getPeriodMean(stat, num_periods);
+ F64 sum_of_squares = 0;
+ S32 valid_period_count = 0;
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ F64 delta = recording.getMean(stat) - period_mean;
+ sum_of_squares += delta * delta;
+ valid_period_count++;
+ }
+ }
+
+ return valid_period_count
+ ? sqrt((F64)sum_of_squares / (F64)valid_period_count)
+ : NaN;
+}
+
+F64 PeriodicRecording::getPeriodMin( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ bool has_value = false;
+ F64 min_val = std::numeric_limits<F64>::max();
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ min_val = llmin(min_val, recording.getMin(stat));
+ has_value = true;
+ }
+ }
+
+ return has_value
+ ? min_val
+ : NaN;
+}
+
+F64 PeriodicRecording::getPeriodMax(const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/)
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ bool has_value = false;
+ F64 max_val = std::numeric_limits<F64>::min();
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ max_val = llmax(max_val, recording.getMax(stat));
+ has_value = true;
+ }
+ }
+
+ return has_value
+ ? max_val
+ : NaN;
+}
+
+
+F64 PeriodicRecording::getPeriodMean( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ S32 valid_period_count = 0;
+ F64 mean = 0;
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ mean += recording.getMean(stat);
+ valid_period_count++;
+ }
+ }
+
+ return valid_period_count
+ ? mean / F64(valid_period_count)
+ : NaN;
+}
+
+F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<SampleAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64 period_mean = getPeriodMean(stat, num_periods);
+ S32 valid_period_count = 0;
+ F64 sum_of_squares = 0;
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ F64 delta = recording.getMean(stat) - period_mean;
+ sum_of_squares += delta * delta;
+ valid_period_count++;
+ }
+ }
+
+ return valid_period_count
+ ? sqrt(sum_of_squares / (F64)valid_period_count)
+ : NaN;
+}
+
+
+F64Kilobytes PeriodicRecording::getPeriodMin( const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64Kilobytes min_val(std::numeric_limits<F64>::max());
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ min_val = llmin(min_val, recording.getMin(stat));
+ }
+
+ return min_val;
+}
+
+F64Kilobytes PeriodicRecording::getPeriodMin(const MemStatHandle& stat, S32 num_periods)
+{
+ return getPeriodMin(static_cast<const StatType<MemAccumulator>&>(stat), num_periods);
+}
+
+F64Kilobytes PeriodicRecording::getPeriodMax(const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/)
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64Kilobytes max_val(0.0);
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ max_val = llmax(max_val, recording.getMax(stat));
+ }
+
+ return max_val;
+}
+
+F64Kilobytes PeriodicRecording::getPeriodMax(const MemStatHandle& stat, S32 num_periods)
+{
+ return getPeriodMax(static_cast<const StatType<MemAccumulator>&>(stat), num_periods);
+}
+
+F64Kilobytes PeriodicRecording::getPeriodMean( const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64Kilobytes mean(0);
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ mean += recording.getMean(stat);
+ }
+
+ return mean / F64(num_periods);
+}
+
+F64Kilobytes PeriodicRecording::getPeriodMean(const MemStatHandle& stat, S32 num_periods)
+{
+ return getPeriodMean(static_cast<const StatType<MemAccumulator>&>(stat), num_periods);
+}
+
+F64Kilobytes PeriodicRecording::getPeriodStandardDeviation( const StatType<MemAccumulator>& stat, S32 num_periods /*= S32_MAX*/ )
+{
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64Kilobytes period_mean = getPeriodMean(stat, num_periods);
+ S32 valid_period_count = 0;
+ F64 sum_of_squares = 0;
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ F64Kilobytes delta = recording.getMean(stat) - period_mean;
+ sum_of_squares += delta.value() * delta.value();
+ valid_period_count++;
+ }
+ }
+
+ return F64Kilobytes(valid_period_count
+ ? sqrt(sum_of_squares / (F64)valid_period_count)
+ : NaN);
+}
+
+F64Kilobytes PeriodicRecording::getPeriodStandardDeviation(const MemStatHandle& stat, S32 num_periods)
+{
+ return getPeriodStandardDeviation(static_cast<const StatType<MemAccumulator>&>(stat), num_periods);
+}
+
+///////////////////////////////////////////////////////////////////////
+// ExtendableRecording
+///////////////////////////////////////////////////////////////////////
+
+void ExtendableRecording::extend()
+{
+ // push the data back to accepted recording
+ mAcceptedRecording.appendRecording(mPotentialRecording);
+ // flush data, so we can start from scratch
+ mPotentialRecording.reset();
+}
+
+void ExtendableRecording::handleStart()
+{
+ mPotentialRecording.start();
+}
+
+void ExtendableRecording::handleStop()
+{
+ mPotentialRecording.pause();
+}
+
+void ExtendableRecording::handleReset()
+{
+ mAcceptedRecording.reset();
+ mPotentialRecording.reset();
+}
+
+void ExtendableRecording::handleSplitTo(ExtendableRecording& other)
+{
+ mPotentialRecording.splitTo(other.mPotentialRecording);
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// ExtendablePeriodicRecording
+///////////////////////////////////////////////////////////////////////
+
+
+ExtendablePeriodicRecording::ExtendablePeriodicRecording()
+: mAcceptedRecording(0),
+ mPotentialRecording(0)
+{}
+
+void ExtendablePeriodicRecording::extend()
+{
+ // push the data back to accepted recording
+ mAcceptedRecording.appendPeriodicRecording(mPotentialRecording);
+ // flush data, so we can start from scratch
+ mPotentialRecording.reset();
+}
+
+
+void ExtendablePeriodicRecording::handleStart()
+{
+ mPotentialRecording.start();
+}
+
+void ExtendablePeriodicRecording::handleStop()
+{
+ mPotentialRecording.pause();
+}
+
+void ExtendablePeriodicRecording::handleReset()
+{
+ mAcceptedRecording.reset();
+ mPotentialRecording.reset();
+}
+
+void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& other)
+{
+ mPotentialRecording.splitTo(other.mPotentialRecording);
+}
+
+
+PeriodicRecording& get_frame_recording()
+{
+ static LLThreadLocalPointer<PeriodicRecording> sRecording(new PeriodicRecording(200, PeriodicRecording::STARTED));
+ return *sRecording;
+}
+
+}
+
+void LLStopWatchControlsMixinCommon::start()
+{
+ switch (mPlayState)
+ {
+ case STOPPED:
+ handleReset();
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case PAUSED:
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case STARTED:
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+}
+
+void LLStopWatchControlsMixinCommon::stop()
+{
+ switch (mPlayState)
+ {
+ case STOPPED:
+ break;
+ case PAUSED:
+ mPlayState = STOPPED;
+ break;
+ case STARTED:
+ handleStop();
+ mPlayState = STOPPED;
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+}
+
+void LLStopWatchControlsMixinCommon::pause()
+{
+ switch (mPlayState)
+ {
+ case STOPPED:
+ // stay stopped, don't go to pause
+ break;
+ case PAUSED:
+ break;
+ case STARTED:
+ handleStop();
+ mPlayState = PAUSED;
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+}
+
+void LLStopWatchControlsMixinCommon::unpause()
+{
+ switch (mPlayState)
+ {
+ case STOPPED:
+ // stay stopped, don't start
+ break;
+ case PAUSED:
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case STARTED:
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+}
+
+void LLStopWatchControlsMixinCommon::resume()
+{
+ switch (mPlayState)
+ {
+ case STOPPED:
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case PAUSED:
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case STARTED:
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+}
+
+void LLStopWatchControlsMixinCommon::restart()
+{
+ switch (mPlayState)
+ {
+ case STOPPED:
+ handleReset();
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case PAUSED:
+ handleReset();
+ handleStart();
+ mPlayState = STARTED;
+ break;
+ case STARTED:
+ handleReset();
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+}
+
+void LLStopWatchControlsMixinCommon::reset()
+{
+ handleReset();
+}
+
+void LLStopWatchControlsMixinCommon::setPlayState( EPlayState state )
+{
+ switch(state)
+ {
+ case STOPPED:
+ stop();
+ break;
+ case PAUSED:
+ pause();
+ break;
+ case STARTED:
+ start();
+ break;
+ default:
+ llassert(false);
+ break;
+ }
+
+ mPlayState = state;
+}
diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h
new file mode 100644
index 0000000000..d0b4a842a6
--- /dev/null
+++ b/indra/llcommon/lltracerecording.h
@@ -0,0 +1,670 @@
+/**
+ * @file lltracerecording.h
+ * @brief Sampling object for collecting runtime statistics originating from lltrace.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTRACERECORDING_H
+#define LL_LLTRACERECORDING_H
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+
+#include "lltimer.h"
+#include "lltraceaccumulators.h"
+#include "llpointer.h"
+
+class LLStopWatchControlsMixinCommon
+{
+public:
+ virtual ~LLStopWatchControlsMixinCommon() {}
+
+ enum EPlayState
+ {
+ STOPPED,
+ PAUSED,
+ STARTED
+ };
+
+ void start(); // moves to started state, resetting if stopped
+ void stop(); // moves to stopped state
+ void pause(); // moves to paused state, unless stopped
+ void unpause(); // moves to started state if paused
+ void resume(); // moves to started state, without resetting
+ void restart(); // moves to started state, always resetting
+ void reset(); // resets
+
+ bool isStarted() const { return mPlayState == STARTED; }
+ bool isPaused() const { return mPlayState == PAUSED; }
+ bool isStopped() const { return mPlayState == STOPPED; }
+
+ EPlayState getPlayState() const { return mPlayState; }
+ // force play state to specific value by calling appropriate handle* methods
+ void setPlayState(EPlayState state);
+
+protected:
+ LLStopWatchControlsMixinCommon()
+ : mPlayState(STOPPED)
+ {}
+
+private:
+ // override these methods to provide started/stopped semantics
+
+ // activate behavior (without reset)
+ virtual void handleStart() = 0;
+ // deactivate behavior
+ virtual void handleStop() = 0;
+ // clear accumulated state, may be called while started
+ virtual void handleReset() = 0;
+
+ EPlayState mPlayState;
+};
+
+template<typename DERIVED>
+class LLStopWatchControlsMixin
+: public LLStopWatchControlsMixinCommon
+{
+public:
+
+ typedef LLStopWatchControlsMixin<DERIVED> self_t;
+ virtual void splitTo(DERIVED& other)
+ {
+ EPlayState play_state = getPlayState();
+ stop();
+ other.reset();
+
+ handleSplitTo(other);
+
+ other.setPlayState(play_state);
+ }
+
+ virtual void splitFrom(DERIVED& other)
+ {
+ static_cast<self_t&>(other).handleSplitTo(*static_cast<DERIVED*>(this));
+ }
+private:
+ self_t& operator = (const self_t& other)
+ {
+ // don't do anything, derived class must implement logic
+ }
+
+ // atomically stop this object while starting the other
+ // no data can be missed in between stop and start
+ virtual void handleSplitTo(DERIVED& other) {};
+
+};
+
+namespace LLTrace
+{
+ template<typename T>
+ class StatType;
+
+ template<typename T>
+ class CountStatHandle;
+
+ template<typename T>
+ class SampleStatHandle;
+
+ template<typename T>
+ class EventStatHandle;
+
+ class MemStatHandle;
+
+ template<typename T>
+ struct RelatedTypes
+ {
+ typedef F64 fractional_t;
+ typedef T sum_t;
+ };
+
+ template<typename T, typename UNIT_T>
+ struct RelatedTypes<LLUnit<T, UNIT_T> >
+ {
+ typedef LLUnit<typename RelatedTypes<T>::fractional_t, UNIT_T> fractional_t;
+ typedef LLUnit<typename RelatedTypes<T>::sum_t, UNIT_T> sum_t;
+ };
+
+ template<>
+ struct RelatedTypes<bool>
+ {
+ typedef F64 fractional_t;
+ typedef S32 sum_t;
+ };
+
+ class Recording
+ : public LLStopWatchControlsMixin<Recording>
+ {
+ public:
+ Recording(EPlayState state = LLStopWatchControlsMixinCommon::STOPPED);
+
+ Recording(const Recording& other);
+ ~Recording();
+
+ Recording& operator = (const Recording& other);
+
+ // accumulate data from subsequent, non-overlapping recording
+ void appendRecording(Recording& other);
+
+ // grab latest recorded data
+ void update();
+
+ // ensure that buffers are exclusively owned by this recording
+ void makeUnique() { mBuffers.makeUnique(); }
+
+ // Timer accessors
+ bool hasValue(const StatType<TimeBlockAccumulator>& stat);
+ F64Seconds getSum(const StatType<TimeBlockAccumulator>& stat);
+ F64Seconds getSum(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat);
+ S32 getSum(const StatType<TimeBlockAccumulator::CallCountFacet>& stat);
+
+ F64Seconds getPerSec(const StatType<TimeBlockAccumulator>& stat);
+ F64Seconds getPerSec(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat);
+ F32 getPerSec(const StatType<TimeBlockAccumulator::CallCountFacet>& stat);
+
+ // Memory accessors
+ bool hasValue(const StatType<MemAccumulator>& stat);
+ F64Kilobytes getMin(const StatType<MemAccumulator>& stat);
+ F64Kilobytes getMean(const StatType<MemAccumulator>& stat);
+ F64Kilobytes getMax(const StatType<MemAccumulator>& stat);
+ F64Kilobytes getStandardDeviation(const StatType<MemAccumulator>& stat);
+ F64Kilobytes getLastValue(const StatType<MemAccumulator>& stat);
+
+ bool hasValue(const StatType<MemAccumulator::AllocationFacet>& stat);
+ F64Kilobytes getSum(const StatType<MemAccumulator::AllocationFacet>& stat);
+ F64Kilobytes getPerSec(const StatType<MemAccumulator::AllocationFacet>& stat);
+ S32 getSampleCount(const StatType<MemAccumulator::AllocationFacet>& stat);
+
+ bool hasValue(const StatType<MemAccumulator::DeallocationFacet>& stat);
+ F64Kilobytes getSum(const StatType<MemAccumulator::DeallocationFacet>& stat);
+ F64Kilobytes getPerSec(const StatType<MemAccumulator::DeallocationFacet>& stat);
+ S32 getSampleCount(const StatType<MemAccumulator::DeallocationFacet>& stat);
+
+ // CountStatHandle accessors
+ bool hasValue(const StatType<CountAccumulator>& stat);
+ F64 getSum(const StatType<CountAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::sum_t getSum(const CountStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::sum_t)getSum(static_cast<const StatType<CountAccumulator>&> (stat));
+ }
+
+ F64 getPerSec(const StatType<CountAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::fractional_t getPerSec(const CountStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::fractional_t)getPerSec(static_cast<const StatType<CountAccumulator>&> (stat));
+ }
+
+ S32 getSampleCount(const StatType<CountAccumulator>& stat);
+
+
+ // SampleStatHandle accessors
+ bool hasValue(const StatType<SampleAccumulator>& stat);
+
+ F64 getMin(const StatType<SampleAccumulator>& stat);
+ template <typename T>
+ T getMin(const SampleStatHandle<T>& stat)
+ {
+ return (T)getMin(static_cast<const StatType<SampleAccumulator>&> (stat));
+ }
+
+ F64 getMax(const StatType<SampleAccumulator>& stat);
+ template <typename T>
+ T getMax(const SampleStatHandle<T>& stat)
+ {
+ return (T)getMax(static_cast<const StatType<SampleAccumulator>&> (stat));
+ }
+
+ F64 getMean(const StatType<SampleAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::fractional_t getMean(SampleStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::fractional_t)getMean(static_cast<const StatType<SampleAccumulator>&> (stat));
+ }
+
+ F64 getStandardDeviation(const StatType<SampleAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::fractional_t getStandardDeviation(const SampleStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::fractional_t)getStandardDeviation(static_cast<const StatType<SampleAccumulator>&> (stat));
+ }
+
+ F64 getLastValue(const StatType<SampleAccumulator>& stat);
+ template <typename T>
+ T getLastValue(const SampleStatHandle<T>& stat)
+ {
+ return (T)getLastValue(static_cast<const StatType<SampleAccumulator>&> (stat));
+ }
+
+ S32 getSampleCount(const StatType<SampleAccumulator>& stat);
+
+ // EventStatHandle accessors
+ bool hasValue(const StatType<EventAccumulator>& stat);
+
+ F64 getSum(const StatType<EventAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::sum_t getSum(const EventStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::sum_t)getSum(static_cast<const StatType<EventAccumulator>&> (stat));
+ }
+
+ F64 getMin(const StatType<EventAccumulator>& stat);
+ template <typename T>
+ T getMin(const EventStatHandle<T>& stat)
+ {
+ return (T)getMin(static_cast<const StatType<EventAccumulator>&> (stat));
+ }
+
+ F64 getMax(const StatType<EventAccumulator>& stat);
+ template <typename T>
+ T getMax(const EventStatHandle<T>& stat)
+ {
+ return (T)getMax(static_cast<const StatType<EventAccumulator>&> (stat));
+ }
+
+ F64 getMean(const StatType<EventAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::fractional_t getMean(EventStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::fractional_t)getMean(static_cast<const StatType<EventAccumulator>&> (stat));
+ }
+
+ F64 getStandardDeviation(const StatType<EventAccumulator>& stat);
+ template <typename T>
+ typename RelatedTypes<T>::fractional_t getStandardDeviation(const EventStatHandle<T>& stat)
+ {
+ return (typename RelatedTypes<T>::fractional_t)getStandardDeviation(static_cast<const StatType<EventAccumulator>&> (stat));
+ }
+
+ F64 getLastValue(const StatType<EventAccumulator>& stat);
+ template <typename T>
+ T getLastValue(const EventStatHandle<T>& stat)
+ {
+ return (T)getLastValue(static_cast<const StatType<EventAccumulator>&> (stat));
+ }
+
+ S32 getSampleCount(const StatType<EventAccumulator>& stat);
+
+ F64Seconds getDuration() const { return mElapsedSeconds; }
+
+ protected:
+ friend class ThreadRecorder;
+
+ // implementation for LLStopWatchControlsMixin
+ /*virtual*/ void handleStart();
+ /*virtual*/ void handleStop();
+ /*virtual*/ void handleReset();
+ /*virtual*/ void handleSplitTo(Recording& other);
+
+ // returns data for current thread
+ class ThreadRecorder* getThreadRecorder();
+
+ LLTimer mSamplingTimer;
+ F64Seconds mElapsedSeconds;
+ LLCopyOnWritePointer<AccumulatorBufferGroup> mBuffers;
+ AccumulatorBufferGroup* mActiveBuffers;
+
+ };
+
+ class LL_COMMON_API PeriodicRecording
+ : public LLStopWatchControlsMixin<PeriodicRecording>
+ {
+ public:
+ PeriodicRecording(S32 num_periods, EPlayState state = STOPPED);
+ ~PeriodicRecording();
+
+ void nextPeriod();
+ S32 getNumRecordedPeriods()
+ {
+ // current period counts if not active
+ return mNumRecordedPeriods + (isStarted() ? 0 : 1);
+ }
+
+ F64Seconds getDuration() const;
+
+ void appendPeriodicRecording(PeriodicRecording& other);
+ void appendRecording(Recording& recording);
+ Recording& getLastRecording();
+ const Recording& getLastRecording() const;
+ Recording& getCurRecording();
+ const Recording& getCurRecording() const;
+ Recording& getPrevRecording(S32 offset);
+ const Recording& getPrevRecording(S32 offset) const;
+ Recording snapshotCurRecording() const;
+
+ template <typename T>
+ S32 getSampleCount(const StatType<T>& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ S32 num_samples = 0;
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ num_samples += recording.getSampleCount(stat);
+ }
+ return num_samples;
+ }
+
+ //
+ // PERIODIC MIN
+ //
+
+ // catch all for stats that have a defined sum
+ template <typename T>
+ typename T::value_t getPeriodMin(const StatType<T>& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ bool has_value = false;
+ typename T::value_t min_val(std::numeric_limits<typename T::value_t>::max());
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ min_val = llmin(min_val, recording.getSum(stat));
+ has_value = true;
+ }
+ }
+
+ return has_value
+ ? min_val
+ : T::getDefaultValue();
+ }
+
+ template<typename T>
+ T getPeriodMin(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return T(getPeriodMin(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+ }
+
+ F64 getPeriodMin(const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ T getPeriodMin(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return T(getPeriodMin(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
+ }
+
+ F64 getPeriodMin(const StatType<EventAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ T getPeriodMin(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return T(getPeriodMin(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
+ }
+
+ F64Kilobytes getPeriodMin(const StatType<MemAccumulator>& stat, S32 num_periods = S32_MAX);
+ F64Kilobytes getPeriodMin(const MemStatHandle& stat, S32 num_periods = S32_MAX);
+
+ template <typename T>
+ typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMinPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ typename RelatedTypes<typename T::value_t>::fractional_t min_val(std::numeric_limits<F64>::max());
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ min_val = llmin(min_val, recording.getPerSec(stat));
+ }
+ return (typename RelatedTypes<typename T::value_t>::fractional_t) min_val;
+ }
+
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodMinPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodMinPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+ }
+
+ //
+ // PERIODIC MAX
+ //
+
+ // catch all for stats that have a defined sum
+ template <typename T>
+ typename T::value_t getPeriodMax(const StatType<T>& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ bool has_value = false;
+ typename T::value_t max_val(std::numeric_limits<typename T::value_t>::min());
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.hasValue(stat))
+ {
+ max_val = llmax(max_val, recording.getSum(stat));
+ has_value = true;
+ }
+ }
+
+ return has_value
+ ? max_val
+ : T::getDefaultValue();
+ }
+
+ template<typename T>
+ T getPeriodMax(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return T(getPeriodMax(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+ }
+
+ F64 getPeriodMax(const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ T getPeriodMax(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return T(getPeriodMax(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
+ }
+
+ F64 getPeriodMax(const StatType<EventAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ T getPeriodMax(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return T(getPeriodMax(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
+ }
+
+ F64Kilobytes getPeriodMax(const StatType<MemAccumulator>& stat, S32 num_periods = S32_MAX);
+ F64Kilobytes getPeriodMax(const MemStatHandle& stat, S32 num_periods = S32_MAX);
+
+ template <typename T>
+ typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMaxPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ F64 max_val = std::numeric_limits<F64>::min();
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ max_val = llmax(max_val, recording.getPerSec(stat));
+ }
+ return (typename RelatedTypes<typename T::value_t>::fractional_t)max_val;
+ }
+
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodMaxPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodMaxPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+ }
+
+ //
+ // PERIODIC MEAN
+ //
+
+ // catch all for stats that have a defined sum
+ template <typename T>
+ typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMean(const StatType<T >& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ typename RelatedTypes<typename T::value_t>::fractional_t mean(0);
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.getDuration() > (F32Seconds)0.f)
+ {
+ mean += recording.getSum(stat);
+ }
+ }
+ return (num_periods
+ ? typename RelatedTypes<typename T::value_t>::fractional_t(mean / num_periods)
+ : typename RelatedTypes<typename T::value_t>::fractional_t(NaN));
+ }
+
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodMean(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+ }
+ F64 getPeriodMean(const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodMean(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
+ }
+
+ F64 getPeriodMean(const StatType<EventAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodMean(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
+ }
+
+ F64Kilobytes getPeriodMean(const StatType<MemAccumulator>& stat, S32 num_periods = S32_MAX);
+ F64Kilobytes getPeriodMean(const MemStatHandle& stat, S32 num_periods = S32_MAX);
+
+ template <typename T>
+ typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMeanPerSec(const StatType<T>& stat, S32 num_periods = S32_MAX)
+ {
+ num_periods = llmin(num_periods, getNumRecordedPeriods());
+
+ typename RelatedTypes<typename T::value_t>::fractional_t mean = 0;
+
+ for (S32 i = 1; i <= num_periods; i++)
+ {
+ Recording& recording = getPrevRecording(i);
+ if (recording.getDuration() > (F32Seconds)0.f)
+ {
+ mean += recording.getPerSec(stat);
+ }
+ }
+
+ return (num_periods
+ ? typename RelatedTypes<typename T::value_t>::fractional_t(mean / num_periods)
+ : typename RelatedTypes<typename T::value_t>::fractional_t(NaN));
+ }
+
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodMeanPerSec(const CountStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodMeanPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods));
+ }
+
+ //
+ // PERIODIC STANDARD DEVIATION
+ //
+
+ F64 getPeriodStandardDeviation(const StatType<SampleAccumulator>& stat, S32 num_periods = S32_MAX);
+
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const SampleStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods));
+ }
+
+ F64 getPeriodStandardDeviation(const StatType<EventAccumulator>& stat, S32 num_periods = S32_MAX);
+ template<typename T>
+ typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const EventStatHandle<T>& stat, S32 num_periods = S32_MAX)
+ {
+ return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<EventAccumulator>&>(stat), num_periods));
+ }
+
+ F64Kilobytes getPeriodStandardDeviation(const StatType<MemAccumulator>& stat, S32 num_periods = S32_MAX);
+ F64Kilobytes getPeriodStandardDeviation(const MemStatHandle& stat, S32 num_periods = S32_MAX);
+
+ private:
+ // implementation for LLStopWatchControlsMixin
+ /*virtual*/ void handleStart();
+ /*virtual*/ void handleStop();
+ /*virtual*/ void handleReset();
+ /*virtual*/ void handleSplitTo(PeriodicRecording& other);
+
+ private:
+ std::vector<Recording> mRecordingPeriods;
+ const bool mAutoResize;
+ S32 mCurPeriod;
+ S32 mNumRecordedPeriods;
+ };
+
+ PeriodicRecording& get_frame_recording();
+
+ class ExtendableRecording
+ : public LLStopWatchControlsMixin<ExtendableRecording>
+ {
+ public:
+ void extend();
+
+ Recording& getAcceptedRecording() { return mAcceptedRecording; }
+ const Recording& getAcceptedRecording() const {return mAcceptedRecording;}
+
+ Recording& getPotentialRecording() { return mPotentialRecording; }
+ const Recording& getPotentialRecording() const { return mPotentialRecording;}
+
+ private:
+ // implementation for LLStopWatchControlsMixin
+ /*virtual*/ void handleStart();
+ /*virtual*/ void handleStop();
+ /*virtual*/ void handleReset();
+ /*virtual*/ void handleSplitTo(ExtendableRecording& other);
+
+ private:
+ Recording mAcceptedRecording;
+ Recording mPotentialRecording;
+ };
+
+ class ExtendablePeriodicRecording
+ : public LLStopWatchControlsMixin<ExtendablePeriodicRecording>
+ {
+ public:
+ ExtendablePeriodicRecording();
+ void extend();
+
+ PeriodicRecording& getResults() { return mAcceptedRecording; }
+ const PeriodicRecording& getResults() const {return mAcceptedRecording;}
+
+ void nextPeriod() { mPotentialRecording.nextPeriod(); }
+
+ private:
+ // implementation for LLStopWatchControlsMixin
+ /*virtual*/ void handleStart();
+ /*virtual*/ void handleStop();
+ /*virtual*/ void handleReset();
+ /*virtual*/ void handleSplitTo(ExtendablePeriodicRecording& other);
+
+ private:
+ PeriodicRecording mAcceptedRecording;
+ PeriodicRecording mPotentialRecording;
+ };
+}
+
+#endif // LL_LLTRACERECORDING_H
diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp
new file mode 100644
index 0000000000..181fc2f058
--- /dev/null
+++ b/indra/llcommon/lltracethreadrecorder.cpp
@@ -0,0 +1,329 @@
+/**
+ * @file lltracethreadrecorder.cpp
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltracethreadrecorder.h"
+#include "llfasttimer.h"
+#include "lltrace.h"
+
+namespace LLTrace
+{
+extern MemStatHandle gTraceMemStat;
+
+static ThreadRecorder* sMasterThreadRecorder = NULL;
+
+///////////////////////////////////////////////////////////////////////
+// ThreadRecorder
+///////////////////////////////////////////////////////////////////////
+
+ThreadRecorder::ThreadRecorder()
+: mParentRecorder(NULL)
+{
+ init();
+}
+
+void ThreadRecorder::init()
+{
+#if LL_TRACE_ENABLED
+ LLThreadLocalSingletonPointer<BlockTimerStackRecord>::setInstance(&mBlockTimerStackRecord);
+ //NB: the ordering of initialization in this function is very fragile due to a large number of implicit dependencies
+ set_thread_recorder(this);
+ BlockTimerStatHandle& root_time_block = BlockTimer::getRootTimeBlock();
+
+ BlockTimerStackRecord* timer_stack = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance();
+ timer_stack->mTimeBlock = &root_time_block;
+ timer_stack->mActiveTimer = NULL;
+
+ mNumTimeBlockTreeNodes = AccumulatorBuffer<TimeBlockAccumulator>::getDefaultBuffer()->size();
+
+ mTimeBlockTreeNodes = new TimeBlockTreeNode[mNumTimeBlockTreeNodes];
+
+ activate(&mThreadRecordingBuffers);
+
+ // initialize time block parent pointers
+ for (BlockTimerStatHandle::instance_tracker_t::instance_iter it = BlockTimerStatHandle::instance_tracker_t::beginInstances(), end_it = BlockTimerStatHandle::instance_tracker_t::endInstances();
+ it != end_it;
+ ++it)
+ {
+ BlockTimerStatHandle& time_block = static_cast<BlockTimerStatHandle&>(*it);
+ TimeBlockTreeNode& tree_node = mTimeBlockTreeNodes[it->getIndex()];
+ tree_node.mBlock = &time_block;
+ tree_node.mParent = &root_time_block;
+
+ it->getCurrentAccumulator().mParent = &root_time_block;
+ }
+
+ mRootTimer = new BlockTimer(root_time_block);
+ timer_stack->mActiveTimer = mRootTimer;
+
+ BlockTimer::getRootTimeBlock().getCurrentAccumulator().mActiveCount = 1;
+
+ claim_alloc(gTraceMemStat, this);
+ claim_alloc(gTraceMemStat, mRootTimer);
+ claim_alloc(gTraceMemStat, sizeof(TimeBlockTreeNode) * mNumTimeBlockTreeNodes);
+#endif
+}
+
+
+ThreadRecorder::ThreadRecorder( ThreadRecorder& parent )
+: mParentRecorder(&parent)
+{
+ init();
+ mParentRecorder->addChildRecorder(this);
+}
+
+
+ThreadRecorder::~ThreadRecorder()
+{
+#if LL_TRACE_ENABLED
+ LLThreadLocalSingletonPointer<BlockTimerStackRecord>::setInstance(NULL);
+
+ disclaim_alloc(gTraceMemStat, this);
+ disclaim_alloc(gTraceMemStat, sizeof(BlockTimer));
+ disclaim_alloc(gTraceMemStat, sizeof(TimeBlockTreeNode) * mNumTimeBlockTreeNodes);
+
+ deactivate(&mThreadRecordingBuffers);
+
+ delete mRootTimer;
+
+ if (!mActiveRecordings.empty())
+ {
+ std::for_each(mActiveRecordings.begin(), mActiveRecordings.end(), DeletePointer());
+ mActiveRecordings.clear();
+ }
+
+ set_thread_recorder(NULL);
+ delete[] mTimeBlockTreeNodes;
+
+ if (mParentRecorder)
+ {
+ mParentRecorder->removeChildRecorder(this);
+ }
+#endif
+}
+
+TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode( S32 index )
+{
+#if LL_TRACE_ENABLED
+ if (0 <= index && index < mNumTimeBlockTreeNodes)
+ {
+ return &mTimeBlockTreeNodes[index];
+ }
+#endif
+ return NULL;
+}
+
+AccumulatorBufferGroup* ThreadRecorder::activate( AccumulatorBufferGroup* recording)
+{
+#if LL_TRACE_ENABLED
+ ActiveRecording* active_recording = new ActiveRecording(recording);
+ if (!mActiveRecordings.empty())
+ {
+ AccumulatorBufferGroup& prev_active_recording = mActiveRecordings.back()->mPartialRecording;
+ prev_active_recording.sync();
+ BlockTimer::updateTimes();
+ prev_active_recording.handOffTo(active_recording->mPartialRecording);
+ }
+ mActiveRecordings.push_back(active_recording);
+
+ mActiveRecordings.back()->mPartialRecording.makeCurrent();
+ return &active_recording->mPartialRecording;
+#else
+ return NULL;
+#endif
+}
+
+ThreadRecorder::active_recording_list_t::iterator ThreadRecorder::bringUpToDate( AccumulatorBufferGroup* recording )
+{
+#if LL_TRACE_ENABLED
+ if (mActiveRecordings.empty()) return mActiveRecordings.end();
+
+ mActiveRecordings.back()->mPartialRecording.sync();
+ BlockTimer::updateTimes();
+
+ active_recording_list_t::reverse_iterator it, end_it;
+ for (it = mActiveRecordings.rbegin(), end_it = mActiveRecordings.rend();
+ it != end_it;
+ ++it)
+ {
+ ActiveRecording* cur_recording = *it;
+
+ active_recording_list_t::reverse_iterator next_it = it;
+ ++next_it;
+
+ // if we have another recording further down in the stack...
+ if (next_it != mActiveRecordings.rend())
+ {
+ // ...push our gathered data down to it
+ (*next_it)->mPartialRecording.append(cur_recording->mPartialRecording);
+ }
+
+ // copy accumulated measurements into result buffer and clear accumulator (mPartialRecording)
+ cur_recording->movePartialToTarget();
+
+ if (cur_recording->mTargetRecording == recording)
+ {
+ // found the recording, so return it
+ break;
+ }
+ }
+
+ if (it == end_it)
+ {
+ LL_WARNS() << "Recording not active on this thread" << LL_ENDL;
+ }
+
+ return (++it).base();
+#else
+ return ThreadRecorder::active_recording_list_t::iterator();
+#endif
+}
+
+void ThreadRecorder::deactivate( AccumulatorBufferGroup* recording )
+{
+#if LL_TRACE_ENABLED
+ active_recording_list_t::iterator recording_it = bringUpToDate(recording);
+ // this method should only be called on a thread where the recorder is active
+ llassert_always(recording_it != mActiveRecordings.end());
+
+ ActiveRecording* recording_to_remove = *recording_it;
+ bool was_current = recording_to_remove->mPartialRecording.isCurrent();
+ llassert(recording_to_remove->mTargetRecording == recording);
+ mActiveRecordings.erase(recording_it);
+ if (was_current)
+ {
+ if (mActiveRecordings.empty())
+ {
+ AccumulatorBufferGroup::clearCurrent();
+ }
+ else
+ {
+ mActiveRecordings.back()->mPartialRecording.makeCurrent();
+ }
+ }
+ delete recording_to_remove;
+#endif
+}
+
+ThreadRecorder::ActiveRecording::ActiveRecording( AccumulatorBufferGroup* target )
+: mTargetRecording(target)
+{}
+
+void ThreadRecorder::ActiveRecording::movePartialToTarget()
+{
+#if LL_TRACE_ENABLED
+ mTargetRecording->append(mPartialRecording);
+ // reset based on self to keep history
+ mPartialRecording.reset(&mPartialRecording);
+#endif
+}
+
+
+// called by child thread
+void ThreadRecorder::addChildRecorder( class ThreadRecorder* child )
+{
+#if LL_TRACE_ENABLED
+ { LLMutexLock lock(&mChildListMutex);
+ mChildThreadRecorders.push_back(child);
+ }
+#endif
+}
+
+// called by child thread
+void ThreadRecorder::removeChildRecorder( class ThreadRecorder* child )
+{
+#if LL_TRACE_ENABLED
+ { LLMutexLock lock(&mChildListMutex);
+ mChildThreadRecorders.remove(child);
+ }
+#endif
+}
+
+void ThreadRecorder::pushToParent()
+{
+#if LL_TRACE_ENABLED
+ { LLMutexLock lock(&mSharedRecordingMutex);
+ LLTrace::get_thread_recorder()->bringUpToDate(&mThreadRecordingBuffers);
+ mSharedRecordingBuffers.append(mThreadRecordingBuffers);
+ mThreadRecordingBuffers.reset();
+ }
+#endif
+}
+
+
+static LLTrace::BlockTimerStatHandle FTM_PULL_TRACE_DATA_FROM_CHILDREN("Pull child thread trace data");
+
+void ThreadRecorder::pullFromChildren()
+{
+#if LL_TRACE_ENABLED
+ LL_RECORD_BLOCK_TIME(FTM_PULL_TRACE_DATA_FROM_CHILDREN);
+ if (mActiveRecordings.empty()) return;
+
+ { LLMutexLock lock(&mChildListMutex);
+
+ AccumulatorBufferGroup& target_recording_buffers = mActiveRecordings.back()->mPartialRecording;
+ target_recording_buffers.sync();
+ for (child_thread_recorder_list_t::iterator it = mChildThreadRecorders.begin(), end_it = mChildThreadRecorders.end();
+ it != end_it;
+ ++it)
+ { LLMutexLock lock(&(*it)->mSharedRecordingMutex);
+
+ target_recording_buffers.merge((*it)->mSharedRecordingBuffers);
+ (*it)->mSharedRecordingBuffers.reset();
+ }
+ }
+#endif
+}
+
+
+void set_master_thread_recorder( ThreadRecorder* recorder )
+{
+ sMasterThreadRecorder = recorder;
+}
+
+
+ThreadRecorder* get_master_thread_recorder()
+{
+ return sMasterThreadRecorder;
+}
+
+LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder_ptr()
+{
+ static LLThreadLocalPointer<ThreadRecorder> s_thread_recorder;
+ return s_thread_recorder;
+}
+
+const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder()
+{
+ return get_thread_recorder_ptr();
+}
+
+void set_thread_recorder( ThreadRecorder* recorder )
+{
+ get_thread_recorder_ptr() = recorder;
+}
+
+}
diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h
new file mode 100644
index 0000000000..a797c6687e
--- /dev/null
+++ b/indra/llcommon/lltracethreadrecorder.h
@@ -0,0 +1,102 @@
+/**
+ * @file lltrace.h
+ * @brief Runtime statistics accumulation.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLTRACETHREADRECORDER_H
+#define LL_LLTRACETHREADRECORDER_H
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+
+#include "llmutex.h"
+#include "lltraceaccumulators.h"
+#include "llthreadlocalstorage.h"
+
+namespace LLTrace
+{
+ class LL_COMMON_API ThreadRecorder
+ {
+ protected:
+ struct ActiveRecording;
+ typedef std::vector<ActiveRecording*> active_recording_list_t;
+ public:
+ ThreadRecorder();
+ explicit ThreadRecorder(ThreadRecorder& parent);
+
+ ~ThreadRecorder();
+
+ AccumulatorBufferGroup* activate(AccumulatorBufferGroup* recording);
+ void deactivate(AccumulatorBufferGroup* recording);
+ active_recording_list_t::iterator bringUpToDate(AccumulatorBufferGroup* recording);
+
+ void addChildRecorder(class ThreadRecorder* child);
+ void removeChildRecorder(class ThreadRecorder* child);
+
+ // call this periodically to gather stats data from child threads
+ void pullFromChildren();
+ void pushToParent();
+
+ TimeBlockTreeNode* getTimeBlockTreeNode(S32 index);
+
+ protected:
+ void init();
+
+ protected:
+ struct ActiveRecording
+ {
+ ActiveRecording(AccumulatorBufferGroup* target);
+
+ AccumulatorBufferGroup* mTargetRecording;
+ AccumulatorBufferGroup mPartialRecording;
+
+ void movePartialToTarget();
+ };
+
+ AccumulatorBufferGroup mThreadRecordingBuffers;
+
+ BlockTimerStackRecord mBlockTimerStackRecord;
+ active_recording_list_t mActiveRecordings;
+
+ class BlockTimer* mRootTimer;
+ TimeBlockTreeNode* mTimeBlockTreeNodes;
+ size_t mNumTimeBlockTreeNodes;
+ typedef std::list<class ThreadRecorder*> child_thread_recorder_list_t;
+
+ child_thread_recorder_list_t mChildThreadRecorders; // list of child thread recorders associated with this master
+ LLMutex mChildListMutex; // protects access to child list
+ LLMutex mSharedRecordingMutex;
+ AccumulatorBufferGroup mSharedRecordingBuffers;
+ ThreadRecorder* mParentRecorder;
+
+ };
+
+ const LLThreadLocalPointer<ThreadRecorder>& get_thread_recorder();
+ void set_thread_recorder(ThreadRecorder*);
+
+ void set_master_thread_recorder(ThreadRecorder*);
+ ThreadRecorder* get_master_thread_recorder();
+}
+
+#endif // LL_LLTRACETHREADRECORDER_H
diff --git a/indra/llcommon/lltreeiterators.h b/indra/llcommon/lltreeiterators.h
index cb1304c54e..ba861ae70b 100644..100755
--- a/indra/llcommon/lltreeiterators.h
+++ b/indra/llcommon/lltreeiterators.h
@@ -34,31 +34,25 @@
* once for each parent. Cycles in the graph will result in either an
* infinite loop or an out-of-memory crash. You Have Been Warned.
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/llunits.h b/indra/llcommon/llunits.h
new file mode 100644
index 0000000000..0fcb8281a0
--- /dev/null
+++ b/indra/llcommon/llunits.h
@@ -0,0 +1,129 @@
+/**
+ * @file llunits.h
+ * @brief Unit definitions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLUNITTYPE_H
+#define LL_LLUNITTYPE_H
+
+#include "stdtypes.h"
+#include "llunittype.h"
+
+//
+// Unit declarations
+//
+
+namespace LLUnits
+{
+LL_DECLARE_BASE_UNIT(Bytes, "B");
+// technically, these are kibibytes, mibibytes, etc. but we should stick with commonly accepted terminology
+LL_DECLARE_DERIVED_UNIT(Kilobytes, "KB", Bytes, / 1024);
+LL_DECLARE_DERIVED_UNIT(Megabytes, "MB", Kilobytes, / 1024);
+LL_DECLARE_DERIVED_UNIT(Gigabytes, "GB", Megabytes, / 1024);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Bytes);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kilobytes);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Megabytes);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Gigabytes);
+
+namespace LLUnits
+{
+// technically, these are kibibits, mibibits, etc. but we should stick with commonly accepted terminology
+LL_DECLARE_DERIVED_UNIT(Bits, "b", Bytes, * 8 );
+LL_DECLARE_DERIVED_UNIT(Kilobits, "Kb", Bits, / 1024);
+LL_DECLARE_DERIVED_UNIT(Megabits, "Mb", Kilobits, / 1024);
+LL_DECLARE_DERIVED_UNIT(Gigabits, "Gb", Megabits, / 1024);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Bits);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kilobits);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Megabits);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Gigabits);
+
+namespace LLUnits
+{
+LL_DECLARE_BASE_UNIT(Seconds, "s");
+LL_DECLARE_DERIVED_UNIT(Minutes, "min", Seconds, / 60);
+LL_DECLARE_DERIVED_UNIT(Hours, "h", Minutes, / 60);
+LL_DECLARE_DERIVED_UNIT(Days, "d", Hours, / 24);
+LL_DECLARE_DERIVED_UNIT(Milliseconds, "ms", Seconds, * 1000);
+LL_DECLARE_DERIVED_UNIT(Microseconds, "\x09\x3cs", Milliseconds, * 1000);
+LL_DECLARE_DERIVED_UNIT(Nanoseconds, "ns", Microseconds, * 1000);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Seconds);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Minutes);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Hours);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Days);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milliseconds);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Microseconds);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Nanoseconds);
+
+namespace LLUnits
+{
+LL_DECLARE_BASE_UNIT(Meters, "m");
+LL_DECLARE_DERIVED_UNIT(Kilometers, "km", Meters, / 1000);
+LL_DECLARE_DERIVED_UNIT(Centimeters, "cm", Meters, * 100);
+LL_DECLARE_DERIVED_UNIT(Millimeters, "mm", Meters, * 1000);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Meters);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kilometers);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Centimeters);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Millimeters);
+
+namespace LLUnits
+{
+// rare units
+LL_DECLARE_BASE_UNIT(Hertz, "Hz");
+LL_DECLARE_DERIVED_UNIT(Kilohertz, "KHz", Hertz, / 1000);
+LL_DECLARE_DERIVED_UNIT(Megahertz, "MHz", Kilohertz, / 1000);
+LL_DECLARE_DERIVED_UNIT(Gigahertz, "GHz", Megahertz, / 1000);
+
+LL_DECLARE_BASE_UNIT(Radians, "rad");
+LL_DECLARE_DERIVED_UNIT(Degrees, "deg", Radians, * 57.29578f);
+
+LL_DECLARE_BASE_UNIT(Percent, "%");
+LL_DECLARE_DERIVED_UNIT(Ratio, "x", Percent, / 100);
+
+LL_DECLARE_BASE_UNIT(Triangles, "tris");
+LL_DECLARE_DERIVED_UNIT(Kilotriangles, "ktris", Triangles, / 1000);
+
+} // namespace LLUnits
+
+// rare units
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Hertz);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kilohertz);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Megahertz);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Gigahertz);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Radians);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Degrees);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Percent);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ratio);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Triangles);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kilotriangles);
+
+
+#endif // LL_LLUNITTYPE_H
diff --git a/indra/llcommon/llunittype.h b/indra/llcommon/llunittype.h
new file mode 100644
index 0000000000..ac8504ca61
--- /dev/null
+++ b/indra/llcommon/llunittype.h
@@ -0,0 +1,838 @@
+/**
+ * @file llunit.h
+ * @brief Unit conversion classes
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_UNITTYPE_H
+#define LL_UNITTYPE_H
+
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+#include "llerror.h"
+
+//lightweight replacement of type traits for simple type equality check
+template<typename S, typename T>
+struct LLIsSameType
+{
+ static const bool value = false;
+};
+
+template<typename T>
+struct LLIsSameType<T, T>
+{
+ static const bool value = true;
+};
+
+// workaround for decltype() not existing and typeof() not working inline in gcc 4.2
+template<typename S, typename T>
+struct LLResultTypeAdd
+{
+ typedef LL_TYPEOF(S() + T()) type_t;
+};
+
+template<typename S, typename T>
+struct LLResultTypeSubtract
+{
+ typedef LL_TYPEOF(S() - T()) type_t;
+};
+
+template<typename S, typename T>
+struct LLResultTypeMultiply
+{
+ typedef LL_TYPEOF(S() * T()) type_t;
+};
+
+template<typename S, typename T>
+struct LLResultTypeDivide
+{
+ typedef LL_TYPEOF(S() / T(1)) type_t;
+};
+
+template<typename S, typename T>
+struct LLResultTypePromote
+{
+ typedef LL_TYPEOF((true) ? S() : T()) type_t;
+};
+
+template<typename STORAGE_TYPE, typename UNITS>
+struct LLUnit
+{
+ typedef LLUnit<STORAGE_TYPE, UNITS> self_t;
+ typedef STORAGE_TYPE storage_t;
+ typedef void is_unit_t;
+
+ // value initialization
+ LL_FORCE_INLINE explicit LLUnit(storage_t value = storage_t())
+ : mValue(value)
+ {}
+
+
+ LL_FORCE_INLINE static self_t convert(self_t v)
+ {
+ return v;
+ }
+
+ template<typename FROM_STORAGE_TYPE>
+ LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, UNITS> v)
+ {
+ self_t result;
+ result.mValue = (STORAGE_TYPE)v.value();
+ return result;
+ }
+
+ template<typename FROM_UNITS>
+ LL_FORCE_INLINE static self_t convert(LLUnit<STORAGE_TYPE, FROM_UNITS> v)
+ {
+ self_t result;
+ STORAGE_TYPE divisor = ll_convert_units(v, result);
+ result.mValue /= divisor;
+ return result;
+ }
+
+ template<typename FROM_STORAGE_TYPE, typename FROM_UNITS>
+ LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, FROM_UNITS> v)
+ {
+ typedef typename LLResultTypePromote<FROM_STORAGE_TYPE, STORAGE_TYPE>::type_t result_storage_t;
+ LLUnit<result_storage_t, UNITS> result;
+ result_storage_t divisor = ll_convert_units(v, result);
+ result.value(result.value() / divisor);
+ return self_t(result.value());
+ }
+
+
+ // unit initialization and conversion
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE LLUnit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+ : mValue(convert(other).mValue)
+ {}
+
+ LL_FORCE_INLINE storage_t value() const
+ {
+ return mValue;
+ }
+
+ LL_FORCE_INLINE void value(storage_t value)
+ {
+ mValue = value;
+ }
+
+ template<typename NEW_UNITS>
+ storage_t valueInUnits()
+ {
+ return LLUnit<storage_t, NEW_UNITS>(*this).value();
+ }
+
+ template<typename NEW_UNITS>
+ void valueInUnits(storage_t value)
+ {
+ *this = LLUnit<storage_t, NEW_UNITS>(value);
+ }
+
+ LL_FORCE_INLINE void operator += (self_t other)
+ {
+ mValue += convert(other).mValue;
+ }
+
+ LL_FORCE_INLINE void operator -= (self_t other)
+ {
+ mValue -= convert(other).mValue;
+ }
+
+ LL_FORCE_INLINE void operator *= (storage_t multiplicand)
+ {
+ mValue *= multiplicand;
+ }
+
+ LL_FORCE_INLINE void operator *= (self_t multiplicand)
+ {
+ // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Multiplication of unit types not supported.");
+ }
+
+ LL_FORCE_INLINE void operator /= (storage_t divisor)
+ {
+ mValue /= divisor;
+ }
+
+ void operator /= (self_t divisor)
+ {
+ // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Illegal in-place division of unit types.");
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return mValue == convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return mValue != convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return mValue < convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return mValue <= convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return mValue > convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return mValue >= convert(other).value();
+ }
+
+protected:
+ storage_t mValue;
+};
+
+template<typename STORAGE_TYPE, typename UNITS>
+std::ostream& operator <<(std::ostream& s, const LLUnit<STORAGE_TYPE, UNITS>& unit)
+{
+ s << unit.value() << UNITS::getUnitLabel();
+ return s;
+}
+
+template<typename STORAGE_TYPE, typename UNITS>
+std::istream& operator >>(std::istream& s, LLUnit<STORAGE_TYPE, UNITS>& unit)
+{
+ STORAGE_TYPE val;
+ s >> val;
+ unit.value(val);
+ return s;
+}
+
+template<typename STORAGE_TYPE, typename UNITS>
+struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNITS>
+{
+ typedef LLUnitImplicit<STORAGE_TYPE, UNITS> self_t;
+ typedef typename LLUnit<STORAGE_TYPE, UNITS>::storage_t storage_t;
+ typedef LLUnit<STORAGE_TYPE, UNITS> base_t;
+
+ LL_FORCE_INLINE LLUnitImplicit(storage_t value = storage_t())
+ : base_t(value)
+ {}
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE LLUnitImplicit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+ : base_t(other)
+ {}
+
+ // unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD value (F32, S32, etc)
+ // this allows for interoperability with legacy code
+ LL_FORCE_INLINE operator storage_t() const
+ {
+ return base_t::value();
+ }
+
+ using base_t::operator +=;
+ LL_FORCE_INLINE void operator += (storage_t value)
+ {
+ base_t::mValue += value;
+ }
+
+ // this overload exists to explicitly catch use of another implicit unit
+ // without ambiguity between conversion to storage_t vs conversion to base_t
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE void operator += (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+ {
+ base_t::mValue += base_t::convert(other).value();
+ }
+
+ using base_t::operator -=;
+ LL_FORCE_INLINE void operator -= (storage_t value)
+ {
+ base_t::mValue -= value;
+ }
+
+ // this overload exists to explicitly catch use of another implicit unit
+ // without ambiguity between conversion to storage_t vs conversion to base_t
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE void operator -= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
+ {
+ base_t::mValue -= base_t::convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue == base_t::convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator == (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue == base_t::convert(other).value();
+ }
+
+ template<typename STORAGE_T>
+ LL_FORCE_INLINE bool operator == (STORAGE_T other) const
+ {
+ return base_t::mValue == other;
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue != convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator != (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue != base_t::convert(other).value();
+ }
+
+ template<typename STORAGE_T>
+ LL_FORCE_INLINE bool operator != (STORAGE_T other) const
+ {
+ return base_t::mValue != other;
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue < base_t::convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator < (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue < base_t::convert(other).value();
+ }
+
+ template<typename STORAGE_T>
+ LL_FORCE_INLINE bool operator < (STORAGE_T other) const
+ {
+ return base_t::mValue < other;
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue <= base_t::convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator <= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue <= base_t::convert(other).value();
+ }
+
+ template<typename STORAGE_T>
+ LL_FORCE_INLINE bool operator <= (STORAGE_T other) const
+ {
+ return base_t::mValue <= other;
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue > base_t::convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator > (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue > base_t::convert(other).value();
+ }
+
+ template<typename STORAGE_T>
+ LL_FORCE_INLINE bool operator > (STORAGE_T other) const
+ {
+ return base_t::mValue > other;
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue >= base_t::convert(other).value();
+ }
+
+ template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
+ LL_FORCE_INLINE bool operator >= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
+ {
+ return base_t::mValue >= base_t::convert(other).value();
+ }
+
+ template<typename STORAGE_T>
+ LL_FORCE_INLINE bool operator >= (STORAGE_T other) const
+ {
+ return base_t::mValue >= other;
+ }
+};
+
+template<typename STORAGE_TYPE, typename UNITS>
+std::ostream& operator <<(std::ostream& s, const LLUnitImplicit<STORAGE_TYPE, UNITS>& unit)
+{
+ s << unit.value() << UNITS::getUnitLabel();
+ return s;
+}
+
+template<typename STORAGE_TYPE, typename UNITS>
+std::istream& operator >>(std::istream& s, LLUnitImplicit<STORAGE_TYPE, UNITS>& unit)
+{
+ STORAGE_TYPE val;
+ s >> val;
+ unit = val;
+ return s;
+}
+
+template<typename S1, typename T1, typename S2, typename T2>
+LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out)
+{
+ S2 divisor(1);
+
+ LL_STATIC_ASSERT((LLIsSameType<T1, T2>::value
+ || !LLIsSameType<T1, typename T1::base_unit_t>::value
+ || !LLIsSameType<T2, typename T2::base_unit_t>::value),
+ "conversion requires compatible units");
+
+ if (LLIsSameType<T1, T2>::value)
+ {
+ // T1 and T2 same type, just assign
+ out.value((S2)in.value());
+ }
+ else if (T1::sLevel > T2::sLevel)
+ {
+ // reduce T1
+ LLUnit<S2, typename T1::base_unit_t> new_in;
+ divisor *= (S2)ll_convert_units(in, new_in);
+ divisor *= (S2)ll_convert_units(new_in, out);
+ }
+ else
+ {
+ // reduce T2
+ LLUnit<S2, typename T2::base_unit_t> new_out;
+ divisor *= (S2)ll_convert_units(in, new_out);
+ divisor *= (S2)ll_convert_units(new_out, out);
+ }
+ return divisor;
+}
+
+template<typename T>
+struct LLStorageType
+{
+ typedef T type_t;
+};
+
+template<typename STORAGE_TYPE, typename UNITS>
+struct LLStorageType<LLUnit<STORAGE_TYPE, UNITS> >
+{
+ typedef STORAGE_TYPE type_t;
+};
+
+// all of these operators need to perform type promotion on the storage type of the units, so they
+// cannot be expressed as operations on identical types with implicit unit conversion
+// e.g. typeof(S32Bytes(x) + F32Megabytes(y)) <==> F32Bytes
+
+//
+// operator +
+//
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result += second;
+ return result;
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
+LLUnit<STORAGE_TYPE, UNITS> operator + (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS second)
+{
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types");
+ return LLUnit<STORAGE_TYPE, UNITS>(0);
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
+LLUnit<STORAGE_TYPE, UNITS> operator + (UNITLESS first, LLUnit<STORAGE_TYPE, UNITS> second)
+{
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types");
+ return LLUnit<STORAGE_TYPE, UNITS>(0);
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result += second;
+ return result;
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result += second;
+ return result;
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result += LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second);
+ return result;
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator + (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
+{
+ LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first);
+ result += second;
+ return result;
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::
+ type_t, UNITS> operator + (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second)
+{
+ LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first);
+ result += second;
+ return result;
+}
+
+//
+// operator -
+//
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result -= second;
+ return result;
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
+LLUnit<STORAGE_TYPE, UNITS> operator - (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS second)
+{
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types");
+ return LLUnit<STORAGE_TYPE, UNITS>(0);
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
+LLUnit<STORAGE_TYPE, UNITS> operator - (UNITLESS first, LLUnit<STORAGE_TYPE, UNITS> second)
+{
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types");
+ return LLUnit<STORAGE_TYPE, UNITS>(0);
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result -= second;
+ return result;
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result -= second;
+ return result;
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
+{
+ LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
+ result -= LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second);
+ return result;
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator - (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
+{
+ LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first);
+ result -= second;
+ return result;
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator - (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second)
+{
+ LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first);
+ result -= second;
+ return result;
+}
+
+//
+// operator *
+//
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LLUnit<STORAGE_TYPE1, UNITS1> operator * (LLUnit<STORAGE_TYPE1, UNITS1>, LLUnit<STORAGE_TYPE2, UNITS2>)
+{
+ // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported.");
+ return LLUnit<STORAGE_TYPE1, UNITS1>();
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator * (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
+{
+ return LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() * second);
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator * (UNITLESS_TYPE first, LLUnit<STORAGE_TYPE, UNITS> second)
+{
+ return LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value());
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LLUnitImplicit<STORAGE_TYPE1, UNITS1> operator * (LLUnitImplicit<STORAGE_TYPE1, UNITS1>, LLUnitImplicit<STORAGE_TYPE2, UNITS2>)
+{
+ // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
+ LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported.");
+ return LLUnitImplicit<STORAGE_TYPE1, UNITS1>();
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator * (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
+{
+ return LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, UNITLESS_TYPE>::type_t, UNITS>(first.value() * second);
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator * (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second)
+{
+ return LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value());
+}
+
+
+//
+// operator /
+//
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator / (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
+{
+ return LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second);
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
+{
+ return first.value() / first.convert(second).value();
+}
+
+template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
+LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator / (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
+{
+ return LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second);
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
+{
+ return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value());
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
+{
+ return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value());
+}
+
+template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
+LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
+{
+ return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value());
+}
+
+template<typename T>
+struct LLGetUnitLabel
+{
+ static const char* getUnitLabel() { return ""; }
+};
+
+template<typename T, typename STORAGE_T>
+struct LLGetUnitLabel<LLUnit<STORAGE_T, T> >
+{
+ static const char* getUnitLabel() { return T::getUnitLabel(); }
+};
+
+template<typename T>
+struct LLUnitLinearOps
+{
+ typedef LLUnitLinearOps<T> self_t;
+
+ LLUnitLinearOps(T val)
+ : mValue(val),
+ mDivisor(1)
+ {}
+
+ template<typename OTHER_T>
+ self_t operator * (OTHER_T other)
+ {
+ return mValue * other;
+ }
+
+ template<typename OTHER_T>
+ self_t operator / (OTHER_T other)
+ {
+ mDivisor *= other;
+ return *this;
+ }
+
+ template<typename OTHER_T>
+ self_t operator + (OTHER_T other)
+ {
+ mValue += other * mDivisor;
+ return *this;
+ }
+
+ template<typename OTHER_T>
+ self_t operator - (OTHER_T other)
+ {
+ mValue -= other * mDivisor;
+ return *this;
+ }
+
+ T mValue;
+ T mDivisor;
+};
+
+template<typename T>
+struct LLUnitInverseLinearOps
+{
+ typedef LLUnitInverseLinearOps<T> self_t;
+
+ LLUnitInverseLinearOps(T val)
+ : mValue(val),
+ mDivisor(1),
+ mMultiplicand(1)
+ {}
+
+ template<typename OTHER_T>
+ self_t operator * (OTHER_T other)
+ {
+ mDivisor *= other;
+ return *this;
+ }
+
+ template<typename OTHER_T>
+ self_t operator / (OTHER_T other)
+ {
+ mValue *= other;
+ mMultiplicand *= other;
+ return *this;
+ }
+
+ template<typename OTHER_T>
+ self_t operator + (OTHER_T other)
+ {
+ mValue -= other * mMultiplicand;
+ return *this;
+ }
+
+ template<typename OTHER_T>
+ self_t operator - (OTHER_T other)
+ {
+ mValue += other * mMultiplicand;
+ return *this;
+ }
+
+ T mValue;
+ T mDivisor;
+ T mMultiplicand;
+};
+
+#define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label) \
+struct base_unit_name \
+{ \
+ static const int sLevel = 0; \
+ typedef base_unit_name base_unit_t; \
+ static const char* getUnitLabel() { return unit_label; } \
+ template<typename T> \
+ static LLUnit<T, base_unit_name> fromValue(T value) { return LLUnit<T, base_unit_name>(value); } \
+ template<typename STORAGE_T, typename UNIT_T> \
+ static LLUnit<STORAGE_T, base_unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value) \
+ { return LLUnit<STORAGE_T, base_unit_name>(value); } \
+}
+
+
+#define LL_DECLARE_DERIVED_UNIT(unit_name, unit_label, base_unit_name, conversion_operation) \
+struct unit_name \
+{ \
+ static const int sLevel = base_unit_name::sLevel + 1; \
+ typedef base_unit_name base_unit_t; \
+ static const char* getUnitLabel() { return unit_label; } \
+ template<typename T> \
+ static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); } \
+ template<typename STORAGE_T, typename UNIT_T> \
+ static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value) \
+ { return LLUnit<STORAGE_T, unit_name>(value); } \
+}; \
+ \
+template<typename S1, typename S2> \
+LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out) \
+{ \
+ typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t; \
+ LLUnitInverseLinearOps<result_storage_t> result = \
+ LLUnitInverseLinearOps<result_storage_t>(in.value()) conversion_operation; \
+ out = LLUnit<S2, base_unit_name>((S2)result.mValue); \
+ return result.mDivisor; \
+} \
+ \
+template<typename S1, typename S2> \
+LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out) \
+{ \
+ typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t; \
+ LLUnitLinearOps<result_storage_t> result = \
+ LLUnitLinearOps<result_storage_t>(in.value()) conversion_operation; \
+ out = LLUnit<S2, unit_name>((S2)result.mValue); \
+ return result.mDivisor; \
+}
+
+#define LL_DECLARE_UNIT_TYPEDEFS(ns, unit_name) \
+ typedef LLUnit<F32, ns::unit_name> F32##unit_name; \
+ typedef LLUnitImplicit<F32, ns::unit_name> F32##unit_name##Implicit;\
+ typedef LLUnit<F64, ns::unit_name> F64##unit_name; \
+ typedef LLUnitImplicit<F64, ns::unit_name> F64##unit_name##Implicit;\
+ typedef LLUnit<S32, ns::unit_name> S32##unit_name; \
+ typedef LLUnitImplicit<S32, ns::unit_name> S32##unit_name##Implicit;\
+ typedef LLUnit<S64, ns::unit_name> S64##unit_name; \
+ typedef LLUnitImplicit<S64, ns::unit_name> S64##unit_name##Implicit;\
+ typedef LLUnit<U32, ns::unit_name> U32##unit_name; \
+ typedef LLUnitImplicit<U32, ns::unit_name> U32##unit_name##Implicit;\
+ typedef LLUnit<U64, ns::unit_name> U64##unit_name; \
+ typedef LLUnitImplicit<U64, ns::unit_name> U64##unit_name##Implicit
+
+#endif //LL_UNITTYPE_H
diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp
index 9d4f3a98f0..9f12d49244 100644..100755
--- a/indra/llcommon/lluri.cpp
+++ b/indra/llcommon/lluri.cpp
@@ -4,31 +4,25 @@
* @date 2006-02-08
* @brief Implementation of the LLURI class.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -43,6 +37,8 @@
// system includes
#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
void encode_character(std::ostream& ostr, std::string::value_type val)
{
@@ -133,11 +129,30 @@ std::string LLURI::unescape(const std::string& str)
{
++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);
+
+ if(is_char_hex(*it))
+ {
+ U8 c = hex_as_nybble(*it++);
+
+ c = c << 4;
+ if (it == end) break;
+
+ if(is_char_hex(*it))
+ {
+ c |= hex_as_nybble(*it);
+ ostr.put((char)c);
+ }
+ else
+ {
+ ostr.put((char)c);
+ ostr.put(*it);
+ }
+ }
+ else
+ {
+ ostr.put('%');
+ ostr.put(*it);
+ }
}
else
{
@@ -231,7 +246,8 @@ static BOOL isDefault(const std::string& scheme, U16 port)
void LLURI::parseAuthorityAndPathUsingOpaque()
{
if (mScheme == "http" || mScheme == "https" ||
- mScheme == "ftp" || mScheme == "secondlife" )
+ mScheme == "ftp" || mScheme == "secondlife" ||
+ mScheme == "x-grid-location-info")
{
if (mEscapedOpaque.substr(0,2) != "//")
{
@@ -322,7 +338,7 @@ LLURI LLURI::buildHTTP(const std::string& prefix,
const LLSD& path)
{
LLURI result;
-
+
// TODO: deal with '/' '?' '#' in host_port
if (prefix.find("://") != prefix.npos)
{
@@ -343,22 +359,48 @@ LLURI LLURI::buildHTTP(const std::string& prefix,
it != path.endArray();
++it)
{
- lldebugs << "PATH: inserting " << it->asString() << llendl;
+ LL_DEBUGS() << "PATH: inserting " << it->asString() << LL_ENDL;
result.mEscapedPath += "/" + escapePathComponent(it->asString());
}
}
- else if(path.isString())
+ else if (path.isString())
{
- result.mEscapedPath += "/" + escapePathComponent(path.asString());
+ std::string pathstr(path);
+ // Trailing slash is significant in HTTP land. If caller specified,
+ // make a point of preserving.
+ std::string last_slash;
+ std::string::size_type len(pathstr.length());
+ if (len && pathstr[len-1] == '/')
+ {
+ last_slash = "/";
+ }
+
+ // Escape every individual path component, recombining with slashes.
+ for (boost::split_iterator<std::string::const_iterator>
+ ti(pathstr, boost::first_finder("/")), tend;
+ ti != tend; ++ti)
+ {
+ // Eliminate a leading slash or duplicate slashes anywhere. (Extra
+ // slashes show up here as empty components.) This test also
+ // eliminates a trailing slash, hence last_slash above.
+ if (! ti->empty())
+ {
+ result.mEscapedPath
+ += "/" + escapePathComponent(std::string(ti->begin(), ti->end()));
+ }
+ }
+
+ // Reinstate trailing slash, if any.
+ result.mEscapedPath += last_slash;
}
else if(path.isUndefined())
{
// do nothing
}
- else
+ else
{
- llwarns << "Valid path arguments to buildHTTP are array, string, or undef, you passed type"
- << path.type() << llendl;
+ LL_WARNS() << "Valid path arguments to buildHTTP are array, string, or undef, you passed type"
+ << path.type() << LL_ENDL;
}
result.mEscapedOpaque = "//" + result.mEscapedAuthority +
result.mEscapedPath;
@@ -542,7 +584,7 @@ LLSD LLURI::queryMap() const
// static
LLSD LLURI::queryMap(std::string escaped_query_string)
{
- lldebugs << "LLURI::queryMap query params: " << escaped_query_string << llendl;
+ LL_DEBUGS() << "LLURI::queryMap query params: " << escaped_query_string << LL_ENDL;
LLSD result = LLSD::emptyArray();
while(!escaped_query_string.empty())
@@ -568,12 +610,12 @@ LLSD LLURI::queryMap(std::string escaped_query_string)
{
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;
+ LL_DEBUGS() << "inserting key " << key << " value " << value << LL_ENDL;
result[key] = value;
}
else
{
- lldebugs << "inserting key " << unescape(tuple) << " value true" << llendl;
+ LL_DEBUGS() << "inserting key " << unescape(tuple) << " value true" << LL_ENDL;
result[unescape(tuple)] = true;
}
}
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h
index 8e69e8558a..c82a666e48 100644..100755
--- a/indra/llcommon/lluri.h
+++ b/indra/llcommon/lluri.h
@@ -4,31 +4,25 @@
* @date 2006-02-05
* @brief Declaration of the URI class.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp
new file mode 100644
index 0000000000..d98bc297e5
--- /dev/null
+++ b/indra/llcommon/lluriparser.cpp
@@ -0,0 +1,261 @@
+/**
+ * @file lluriparser.cpp
+ * @author Protey
+ * @date 2014-10-07
+ * @brief Implementation of the LLUriParser class.
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "lluriparser.h"
+
+LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedTmp(false), mRes(0)
+{
+ mState.uri = &mUri;
+
+ if (u.find("://") == std::string::npos)
+ {
+ mNormalizedUri = "http://";
+ mTmpScheme = true;
+ }
+
+ mNormalizedUri += u.c_str();
+
+ mRes = parse();
+}
+
+LLUriParser::~LLUriParser()
+{
+ uriFreeUriMembersA(&mUri);
+}
+
+S32 LLUriParser::parse()
+{
+ mRes = uriParseUriA(&mState, mNormalizedUri.c_str());
+ return mRes;
+}
+
+const char * LLUriParser::scheme() const
+{
+ return mScheme.c_str();
+}
+
+void LLUriParser::sheme(const std::string& s)
+{
+ mTmpScheme = !s.size();
+ mScheme = s;
+}
+
+const char * LLUriParser::port() const
+{
+ return mPort.c_str();
+}
+
+void LLUriParser::port(const std::string& s)
+{
+ mPort = s;
+}
+
+const char * LLUriParser::host() const
+{
+ return mHost.c_str();
+}
+
+void LLUriParser::host(const std::string& s)
+{
+ mHost = s;
+}
+
+const char * LLUriParser::path() const
+{
+ return mPath.c_str();
+}
+
+void LLUriParser::path(const std::string& s)
+{
+ mPath = s;
+}
+
+const char * LLUriParser::query() const
+{
+ return mQuery.c_str();
+}
+
+void LLUriParser::query(const std::string& s)
+{
+ mQuery = s;
+}
+
+const char * LLUriParser::fragment() const
+{
+ return mFragment.c_str();
+}
+
+void LLUriParser::fragment(const std::string& s)
+{
+ mFragment = s;
+}
+
+void LLUriParser::textRangeToString(UriTextRangeA& textRange, std::string& str)
+{
+ if (textRange.first != NULL && textRange.afterLast != NULL && textRange.first < textRange.afterLast)
+ {
+ const ptrdiff_t len = textRange.afterLast - textRange.first;
+ str.assign(textRange.first, static_cast<std::string::size_type>(len));
+ }
+ else
+ {
+ str = LLStringUtil::null;
+ }
+}
+
+void LLUriParser::extractParts()
+{
+ if (mTmpScheme || mNormalizedTmp)
+ {
+ mScheme.clear();
+ }
+ else
+ {
+ textRangeToString(mUri.scheme, mScheme);
+ }
+
+ textRangeToString(mUri.hostText, mHost);
+ textRangeToString(mUri.portText, mPort);
+ textRangeToString(mUri.query, mQuery);
+ textRangeToString(mUri.fragment, mFragment);
+
+ UriPathSegmentA * pathHead = mUri.pathHead;
+ while (pathHead)
+ {
+ std::string partOfPath;
+ textRangeToString(pathHead->text, partOfPath);
+
+ mPath += '/';
+ mPath += partOfPath;
+
+ pathHead = pathHead->next;
+ }
+}
+
+S32 LLUriParser::normalize()
+{
+ mNormalizedTmp = mTmpScheme;
+ if (!mRes)
+ {
+ mRes = uriNormalizeSyntaxExA(&mUri, URI_NORMALIZE_SCHEME | URI_NORMALIZE_HOST);
+
+ if (!mRes)
+ {
+ S32 chars_required;
+ mRes = uriToStringCharsRequiredA(&mUri, &chars_required);
+
+ if (!mRes)
+ {
+ chars_required++;
+ std::vector<char> label_buf(chars_required);
+ mRes = uriToStringA(&label_buf[0], &mUri, chars_required, NULL);
+
+ if (!mRes)
+ {
+ mNormalizedUri = &label_buf[mTmpScheme ? 7 : 0];
+ mTmpScheme = false;
+ }
+ }
+ }
+ }
+
+ if(mTmpScheme)
+ {
+ mNormalizedUri = mNormalizedUri.substr(7);
+ mTmpScheme = false;
+ }
+
+ return mRes;
+}
+
+void LLUriParser::glue(std::string& uri) const
+{
+ std::string first_part;
+ glueFirst(first_part);
+
+ std::string second_part;
+ glueSecond(second_part);
+
+ uri = first_part + second_part;
+}
+
+void LLUriParser::glueFirst(std::string& uri) const
+{
+ if (mScheme.size())
+ {
+ uri = mScheme;
+ uri += "://";
+ }
+ else
+ {
+ uri.clear();
+ }
+
+ uri += mHost;
+}
+
+void LLUriParser::glueSecond(std::string& uri) const
+{
+ if (mPort.size())
+ {
+ uri = ':';
+ uri += mPort;
+ }
+ else
+ {
+ uri.clear();
+ }
+
+ uri += mPath;
+
+ if (mQuery.size())
+ {
+ uri += '?';
+ uri += mQuery;
+
+ if (mFragment.size())
+ {
+ uri += '#';
+ uri += mFragment;
+ }
+ }
+}
+
+bool LLUriParser::test() const
+{
+ std::string uri;
+ glue(uri);
+
+ return uri == mNormalizedUri;
+}
+
+const char * LLUriParser::normalizedUri() const
+{
+ return mNormalizedUri.c_str();
+}
diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h
new file mode 100644
index 0000000000..2df8085ae6
--- /dev/null
+++ b/indra/llcommon/lluriparser.h
@@ -0,0 +1,87 @@
+/**
+ * @file lluriparser.h
+ * @author Protey
+ * @date 20146-10-07
+ * @brief Declaration of the UriParser class.
+ *
+ * $LicenseInfo:firstyear=2014&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2014, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURIPARSER_H
+#define LL_LLURIPARSER_H
+
+#include <string>
+#include "uriparser/Uri.h"
+
+class LL_COMMON_API LLUriParser
+{
+public:
+ LLUriParser(const std::string& u);
+ ~LLUriParser();
+
+ const char * scheme() const;
+ void sheme (const std::string& s);
+
+ const char * port() const;
+ void port (const std::string& s);
+
+ const char * host() const;
+ void host (const std::string& s);
+
+ const char * path() const;
+ void path (const std::string& s);
+
+ const char * query() const;
+ void query (const std::string& s);
+
+ const char * fragment() const;
+ void fragment (const std::string& s);
+
+ const char * normalizedUri() const;
+
+ void extractParts();
+ void glue(std::string& uri) const;
+ void glueFirst(std::string& uri) const;
+ void glueSecond(std::string& uri) const;
+ bool test() const;
+ S32 normalize();
+
+private:
+ S32 parse();
+ void textRangeToString(UriTextRangeA& textRange, std::string& str);
+ std::string mScheme;
+ std::string mHost;
+ std::string mPort;
+ std::string mPath;
+ std::string mQuery;
+ std::string mFragment;
+ std::string mNormalizedUri;
+
+ UriParserStateA mState;
+ UriUriA mUri;
+
+ S32 mRes;
+ bool mTmpScheme;
+ bool mNormalizedTmp;
+};
+
+#endif // LL_LLURIPARSER_H
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index bcbae06ec5..e3671047b4 100644..100755
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -1,31 +1,25 @@
/**
* @file lluuid.cpp
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -33,9 +27,10 @@
// We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes.
#if LL_WINDOWS
-# undef WIN32_LEAN_AND_MEAN
-# include <winsock2.h>
-# include <windows.h>
+#include "llwin32headers.h"
+// ugh, this is ugly. We need to straighten out our linking for this library
+#pragma comment(lib, "IPHLPAPI.lib")
+#include <iphlpapi.h>
#endif
#include "lldefs.h"
@@ -47,10 +42,16 @@
#include "llmd5.h"
#include "llstring.h"
#include "lltimer.h"
+#include "llthread.h"
const LLUUID LLUUID::null;
const LLTransactionID LLTransactionID::tnull;
+// static
+LLMutex * LLUUID::mMutex = NULL;
+
+
+
/*
NOT DONE YET!!!
@@ -231,7 +232,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
{
if(emit)
{
- llwarns << "Warning! Using broken UUID string format" << llendl;
+ LL_WARNS() << "Warning! Using broken UUID string format" << LL_ENDL;
}
broken_format = TRUE;
}
@@ -241,7 +242,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
if(emit)
{
//don't spam the logs because a resident can't spell.
- llwarns << "Bad UUID string: " << in_string << llendl;
+ LL_WARNS() << "Bad UUID string: " << in_string << LL_ENDL;
}
setNull();
return FALSE;
@@ -280,7 +281,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
{
if(emit)
{
- llwarns << "Invalid UUID string character" << llendl;
+ LL_WARNS() << "Invalid UUID string character" << LL_ENDL;
}
setNull();
return FALSE;
@@ -305,7 +306,7 @@ BOOL LLUUID::set(const std::string& in_string, BOOL emit)
{
if(emit)
{
- llwarns << "Invalid UUID string character" << llendl;
+ LL_WARNS() << "Invalid UUID string character" << LL_ENDL;
}
setNull();
return FALSE;
@@ -452,7 +453,8 @@ static void get_random_bytes(void *buf, int nbytes)
return;
}
-#if LL_WINDOWS
+#if LL_WINDOWS
+
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
@@ -460,58 +462,44 @@ typedef struct _ASTAT_
}ASTAT, * PASTAT;
// static
-S32 LLUUID::getNodeID(unsigned char * node_id)
-{
- ASTAT Adapter;
- NCB Ncb;
- UCHAR uRetCode;
- LANA_ENUM lenum;
- int i;
- int retval = 0;
-
- memset( &Ncb, 0, sizeof(Ncb) );
- Ncb.ncb_command = NCBENUM;
- Ncb.ncb_buffer = (UCHAR *)&lenum;
- Ncb.ncb_length = sizeof(lenum);
- uRetCode = Netbios( &Ncb );
- // printf( "The NCBENUM return code is: 0x%x \n", uRetCode );
-
- for(i=0; i < lenum.length ;i++)
- {
- memset( &Ncb, 0, sizeof(Ncb) );
- Ncb.ncb_command = NCBRESET;
- Ncb.ncb_lana_num = lenum.lana[i];
-
- uRetCode = Netbios( &Ncb );
- // printf( "The NCBRESET on LANA %d return code is: 0x%x \n",
- // lenum.lana[i], uRetCode );
-
- memset( &Ncb, 0, sizeof (Ncb) );
- Ncb.ncb_command = NCBASTAT;
- Ncb.ncb_lana_num = lenum.lana[i];
-
- strcpy( (char *)Ncb.ncb_callname, "* " ); /* Flawfinder: ignore */
- Ncb.ncb_buffer = (unsigned char *)&Adapter;
- Ncb.ncb_length = sizeof(Adapter);
-
- uRetCode = Netbios( &Ncb );
-// printf( "The NCBASTAT on LANA %d return code is: 0x%x \n",
-// lenum.lana[i], uRetCode );
- if ( uRetCode == 0 )
- {
-// printf( "The Ethernet Number on LANA %d is: %02x%02x%02x%02x%02x%02x\n",
-// lenum.lana[i],
-// Adapter.adapt.adapter_address[0],
-// Adapter.adapt.adapter_address[1],
-// Adapter.adapt.adapter_address[2],
-// Adapter.adapt.adapter_address[3],
-// Adapter.adapt.adapter_address[4],
-// Adapter.adapt.adapter_address[5] );
+S32 LLUUID::getNodeID(unsigned char *node_id)
+{
+ ASTAT Adapter;
+ NCB Ncb;
+ UCHAR uRetCode;
+ LANA_ENUM lenum;
+ int i;
+ int retval = 0;
+
+ memset( &Ncb, 0, sizeof(Ncb) );
+ Ncb.ncb_command = NCBENUM;
+ Ncb.ncb_buffer = (UCHAR *)&lenum;
+ Ncb.ncb_length = sizeof(lenum);
+ uRetCode = Netbios( &Ncb );
+
+ for(i=0; i < lenum.length ;i++)
+ {
+ memset( &Ncb, 0, sizeof(Ncb) );
+ Ncb.ncb_command = NCBRESET;
+ Ncb.ncb_lana_num = lenum.lana[i];
+
+ uRetCode = Netbios( &Ncb );
+
+ memset( &Ncb, 0, sizeof (Ncb) );
+ Ncb.ncb_command = NCBASTAT;
+ Ncb.ncb_lana_num = lenum.lana[i];
+
+ strcpy( (char *)Ncb.ncb_callname, "* " ); /* Flawfinder: ignore */
+ Ncb.ncb_buffer = (unsigned char *)&Adapter;
+ Ncb.ncb_length = sizeof(Adapter);
+
+ uRetCode = Netbios( &Ncb );
+ if ( uRetCode == 0 )
+ {
memcpy(node_id,Adapter.adapt.adapter_address,6); /* Flawfinder: ignore */
retval = 1;
-
- }
- }
+ }
+ }
return retval;
}
@@ -750,6 +738,7 @@ void LLUUID::getCurrentTime(uuid_time_t *timestamp)
getSystemTime(&time_last);
uuids_this_tick = uuids_per_tick;
init = TRUE;
+ mMutex = new LLMutex(NULL);
}
uuid_time_t time_now = {0,0};
@@ -801,6 +790,7 @@ void LLUUID::generate()
#endif
if (!has_init)
{
+ has_init = 1;
if (getNodeID(node_id) <= 0)
{
get_random_bytes(node_id, 6);
@@ -822,18 +812,24 @@ void LLUUID::generate()
#else
clock_seq = (U16)ll_rand(65536);
#endif
- has_init = 1;
}
// get current time
getCurrentTime(&timestamp);
+ U16 our_clock_seq = clock_seq;
- // if clock went backward change clockseq
- if (cmpTime(&timestamp, &time_last) == -1) {
+ // if clock hasn't changed or went backward, change clockseq
+ if (cmpTime(&timestamp, &time_last) != 1)
+ {
+ LLMutexLock lock(mMutex);
clock_seq = (clock_seq + 1) & 0x3FFF;
- if (clock_seq == 0) clock_seq++;
+ if (clock_seq == 0)
+ clock_seq++;
+ our_clock_seq = clock_seq; // Ensure we're using a different clock_seq value from previous time
}
+ time_last = timestamp;
+
memcpy(mData+10, node_id, 6); /* Flawfinder: ignore */
U32 tmp;
tmp = timestamp.low;
@@ -855,7 +851,8 @@ void LLUUID::generate()
tmp >>= 8;
mData[6] = (unsigned char) tmp;
- tmp = clock_seq;
+ tmp = our_clock_seq;
+
mData[9] = (unsigned char) tmp;
tmp >>= 8;
mData[8] = (unsigned char) tmp;
@@ -865,8 +862,6 @@ void LLUUID::generate()
md5_uuid.update(mData,16);
md5_uuid.finalize();
md5_uuid.raw_digest(mData);
-
- time_last = timestamp;
}
void LLUUID::generate(const std::string& hash_string)
@@ -880,8 +875,14 @@ U32 LLUUID::getRandomSeed()
static unsigned char seed[16]; /* Flawfinder: ignore */
getNodeID(&seed[0]);
- seed[6]='\0';
- seed[7]='\0';
+
+ // Incorporate the pid into the seed to prevent
+ // processes that start on the same host at the same
+ // time from generating the same seed.
+ pid_t pid = LLApp::getPid();
+
+ seed[6]=(unsigned char)(pid >> 8);
+ seed[7]=(unsigned char)(pid);
getSystemTime((uuid_time_t *)(&seed[8]));
LLMD5 md5_seed;
@@ -938,3 +939,174 @@ LLAssetID LLTransactionID::makeAssetID(const LLUUID& session) const
}
return result;
}
+
+// Construct
+LLUUID::LLUUID()
+{
+ setNull();
+}
+
+
+// Faster than copying from memory
+ void LLUUID::setNull()
+{
+ U32 *word = (U32 *)mData;
+ word[0] = 0;
+ word[1] = 0;
+ word[2] = 0;
+ word[3] = 0;
+}
+
+
+// Compare
+ bool LLUUID::operator==(const LLUUID& rhs) const
+{
+ U32 *tmp = (U32 *)mData;
+ U32 *rhstmp = (U32 *)rhs.mData;
+ // Note: binary & to avoid branching
+ return
+ (tmp[0] == rhstmp[0]) &
+ (tmp[1] == rhstmp[1]) &
+ (tmp[2] == rhstmp[2]) &
+ (tmp[3] == rhstmp[3]);
+}
+
+
+ bool LLUUID::operator!=(const LLUUID& rhs) const
+{
+ U32 *tmp = (U32 *)mData;
+ U32 *rhstmp = (U32 *)rhs.mData;
+ // Note: binary | to avoid branching
+ return
+ (tmp[0] != rhstmp[0]) |
+ (tmp[1] != rhstmp[1]) |
+ (tmp[2] != rhstmp[2]) |
+ (tmp[3] != rhstmp[3]);
+}
+
+/*
+// JC: This is dangerous. It allows UUIDs to be cast automatically
+// to integers, among other things. Use isNull() or notNull().
+ LLUUID::operator bool() const
+{
+ U32 *word = (U32 *)mData;
+ return (word[0] | word[1] | word[2] | word[3]) > 0;
+}
+*/
+
+ BOOL LLUUID::notNull() const
+{
+ U32 *word = (U32 *)mData;
+ return (word[0] | word[1] | word[2] | word[3]) > 0;
+}
+
+// Faster than == LLUUID::null because doesn't require
+// as much memory access.
+ BOOL LLUUID::isNull() const
+{
+ U32 *word = (U32 *)mData;
+ // If all bits are zero, return !0 == TRUE
+ return !(word[0] | word[1] | word[2] | word[3]);
+}
+
+// Copy constructor
+ LLUUID::LLUUID(const LLUUID& rhs)
+{
+ U32 *tmp = (U32 *)mData;
+ U32 *rhstmp = (U32 *)rhs.mData;
+ tmp[0] = rhstmp[0];
+ tmp[1] = rhstmp[1];
+ tmp[2] = rhstmp[2];
+ tmp[3] = rhstmp[3];
+}
+
+ LLUUID::~LLUUID()
+{
+}
+
+// Assignment
+ LLUUID& LLUUID::operator=(const LLUUID& rhs)
+{
+ // No need to check the case where this==&rhs. The branch is slower than the write.
+ U32 *tmp = (U32 *)mData;
+ U32 *rhstmp = (U32 *)rhs.mData;
+ tmp[0] = rhstmp[0];
+ tmp[1] = rhstmp[1];
+ tmp[2] = rhstmp[2];
+ tmp[3] = rhstmp[3];
+
+ return *this;
+}
+
+
+ LLUUID::LLUUID(const char *in_string)
+{
+ if (!in_string || in_string[0] == 0)
+ {
+ setNull();
+ return;
+ }
+
+ set(in_string);
+}
+
+ LLUUID::LLUUID(const std::string& in_string)
+{
+ if (in_string.empty())
+ {
+ setNull();
+ return;
+ }
+
+ set(in_string);
+}
+
+// IW: DON'T "optimize" these w/ U32s or you'll scoogie the sort order
+// IW: this will make me very sad
+ bool LLUUID::operator<(const LLUUID &rhs) const
+{
+ U32 i;
+ for( i = 0; i < (UUID_BYTES - 1); i++ )
+ {
+ if( mData[i] != rhs.mData[i] )
+ {
+ return (mData[i] < rhs.mData[i]);
+ }
+ }
+ return (mData[UUID_BYTES - 1] < rhs.mData[UUID_BYTES - 1]);
+}
+
+ bool LLUUID::operator>(const LLUUID &rhs) const
+{
+ U32 i;
+ for( i = 0; i < (UUID_BYTES - 1); i++ )
+ {
+ if( mData[i] != rhs.mData[i] )
+ {
+ return (mData[i] > rhs.mData[i]);
+ }
+ }
+ return (mData[UUID_BYTES - 1] > rhs.mData[UUID_BYTES - 1]);
+}
+
+ U16 LLUUID::getCRC16() const
+{
+ // A UUID is 16 bytes, or 8 shorts.
+ U16 *short_data = (U16*)mData;
+ U16 out = 0;
+ out += short_data[0];
+ out += short_data[1];
+ out += short_data[2];
+ out += short_data[3];
+ out += short_data[4];
+ out += short_data[5];
+ out += short_data[6];
+ out += short_data[7];
+ return out;
+}
+
+ U32 LLUUID::getCRC32() const
+{
+ U32 *tmp = (U32*)mData;
+ return tmp[0] + tmp[1] + tmp[2] + tmp[3];
+}
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index c78fb12018..dd8660a3c8 100644..100755
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -1,31 +1,25 @@
/**
* @file lluuid.h
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -34,9 +28,12 @@
#include <iostream>
#include <set>
+#include <vector>
#include "stdtypes.h"
#include "llpreprocessor.h"
+class LLMutex;
+
const S32 UUID_BYTES = 16;
const S32 UUID_WORDS = 4;
const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below
@@ -124,6 +121,7 @@ public:
static BOOL validate(const std::string& in_string); // Validate that the UUID string is legal.
static const LLUUID null;
+ static LLMutex * mMutex;
static U32 getRandomSeed();
static S32 getNodeID(unsigned char * node_id);
@@ -133,181 +131,15 @@ public:
U8 mData[UUID_BYTES];
};
+typedef std::vector<LLUUID> uuid_vec_t;
+typedef std::set<LLUUID> uuid_set_t;
-// Construct
-inline LLUUID::LLUUID()
-{
- setNull();
-}
-
-
-// Faster than copying from memory
-inline void LLUUID::setNull()
-{
- U32 *word = (U32 *)mData;
- word[0] = 0;
- word[1] = 0;
- word[2] = 0;
- word[3] = 0;
-}
-
-
-// Compare
-inline bool LLUUID::operator==(const LLUUID& rhs) const
-{
- U32 *tmp = (U32 *)mData;
- U32 *rhstmp = (U32 *)rhs.mData;
- // Note: binary & to avoid branching
- return
- (tmp[0] == rhstmp[0]) &
- (tmp[1] == rhstmp[1]) &
- (tmp[2] == rhstmp[2]) &
- (tmp[3] == rhstmp[3]);
-}
-
-
-inline bool LLUUID::operator!=(const LLUUID& rhs) const
-{
- U32 *tmp = (U32 *)mData;
- U32 *rhstmp = (U32 *)rhs.mData;
- // Note: binary | to avoid branching
- return
- (tmp[0] != rhstmp[0]) |
- (tmp[1] != rhstmp[1]) |
- (tmp[2] != rhstmp[2]) |
- (tmp[3] != rhstmp[3]);
-}
-
-/*
-// JC: This is dangerous. It allows UUIDs to be cast automatically
-// to integers, among other things. Use isNull() or notNull().
-inline LLUUID::operator bool() const
-{
- U32 *word = (U32 *)mData;
- return (word[0] | word[1] | word[2] | word[3]) > 0;
-}
-*/
-
-inline BOOL LLUUID::notNull() const
-{
- U32 *word = (U32 *)mData;
- return (word[0] | word[1] | word[2] | word[3]) > 0;
-}
-
-// Faster than == LLUUID::null because doesn't require
-// as much memory access.
-inline BOOL LLUUID::isNull() const
-{
- U32 *word = (U32 *)mData;
- // If all bits are zero, return !0 == TRUE
- return !(word[0] | word[1] | word[2] | word[3]);
-}
-
-// Copy constructor
-inline LLUUID::LLUUID(const LLUUID& rhs)
-{
- U32 *tmp = (U32 *)mData;
- U32 *rhstmp = (U32 *)rhs.mData;
- tmp[0] = rhstmp[0];
- tmp[1] = rhstmp[1];
- tmp[2] = rhstmp[2];
- tmp[3] = rhstmp[3];
-}
-
-inline LLUUID::~LLUUID()
-{
-}
-
-// Assignment
-inline LLUUID& LLUUID::operator=(const LLUUID& rhs)
-{
- // No need to check the case where this==&rhs. The branch is slower than the write.
- U32 *tmp = (U32 *)mData;
- U32 *rhstmp = (U32 *)rhs.mData;
- tmp[0] = rhstmp[0];
- tmp[1] = rhstmp[1];
- tmp[2] = rhstmp[2];
- tmp[3] = rhstmp[3];
-
- return *this;
-}
-
-
-inline LLUUID::LLUUID(const char *in_string)
-{
- if (!in_string || in_string[0] == 0)
- {
- setNull();
- return;
- }
-
- set(in_string);
-}
-
-inline LLUUID::LLUUID(const std::string& in_string)
-{
- if (in_string.empty())
- {
- setNull();
- return;
- }
-
- set(in_string);
-}
-
-// IW: DON'T "optimize" these w/ U32s or you'll scoogie the sort order
-// IW: this will make me very sad
-inline bool LLUUID::operator<(const LLUUID &rhs) const
-{
- U32 i;
- for( i = 0; i < (UUID_BYTES - 1); i++ )
- {
- if( mData[i] != rhs.mData[i] )
- {
- return (mData[i] < rhs.mData[i]);
- }
- }
- return (mData[UUID_BYTES - 1] < rhs.mData[UUID_BYTES - 1]);
-}
-
-inline bool LLUUID::operator>(const LLUUID &rhs) const
-{
- U32 i;
- for( i = 0; i < (UUID_BYTES - 1); i++ )
- {
- if( mData[i] != rhs.mData[i] )
- {
- return (mData[i] > rhs.mData[i]);
- }
- }
- return (mData[UUID_BYTES - 1] > rhs.mData[UUID_BYTES - 1]);
-}
-
-inline U16 LLUUID::getCRC16() const
-{
- // A UUID is 16 bytes, or 8 shorts.
- U16 *short_data = (U16*)mData;
- U16 out = 0;
- out += short_data[0];
- out += short_data[1];
- out += short_data[2];
- out += short_data[3];
- out += short_data[4];
- out += short_data[5];
- out += short_data[6];
- out += short_data[7];
- return out;
-}
-
-inline U32 LLUUID::getCRC32() const
-{
- U32 *tmp = (U32*)mData;
- return tmp[0] + tmp[1] + tmp[2] + tmp[3];
-}
-
-
-// Helper structure for ordering lluuids in stl containers.
-// eg: std::map<LLUUID, LLWidget*, lluuid_less> widget_map;
+// Helper structure for ordering lluuids in stl containers. eg:
+// std::map<LLUUID, LLWidget*, lluuid_less> widget_map;
+//
+// (isn't this the default behavior anyway? I think we could
+// everywhere replace these with uuid_set_t, but someone should
+// verify.)
struct lluuid_less
{
bool operator()(const LLUUID& lhs, const LLUUID& rhs) const
@@ -317,7 +149,6 @@ struct lluuid_less
};
typedef std::set<LLUUID, lluuid_less> uuid_list_t;
-
/*
* Sub-classes for keeping transaction IDs and asset IDs
* straight.
@@ -334,3 +165,5 @@ public:
};
#endif
+
+
diff --git a/indra/llcommon/lluuidhashmap.h b/indra/llcommon/lluuidhashmap.h
deleted file mode 100644
index c45e413996..0000000000
--- a/indra/llcommon/lluuidhashmap.h
+++ /dev/null
@@ -1,589 +0,0 @@
-/**
- * @file lluuidhashmap.h
- * @brief A uuid based hash map.
- *
- * $LicenseInfo:firstyear=2003&license=viewergpl$
- *
- * Copyright (c) 2003-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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 = (LLUUIDHashNode<DATA_TYPE, SIZE>*) &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 reset();
- 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()
-{
- reset();
-}
-
-template <class DATA_TYPE, int SIZE>
-inline void LLUUIDHashMapIter<DATA_TYPE, SIZE>::reset()
-{
- if (mCurHashNodep)
- {
- // We're partway through an iteration, we can clean up now
- mHashMapp->mIterCount--;
- mCurHashNodep = NULL;
- }
-}
-
-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/llversionserver.h b/indra/llcommon/llversionserver.h
deleted file mode 100644
index 0f1e59a18c..0000000000
--- a/indra/llcommon/llversionserver.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/**
- * @file llversionserver.h
- * @brief
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLVERSIONSERVER_H
-#define LL_LLVERSIONSERVER_H
-
-const S32 LL_VERSION_MAJOR = 1;
-const S32 LL_VERSION_MINOR = 31;
-const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 200030;
-
-const char * const LL_CHANNEL = "Second Life Server";
-
-
-#endif
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
deleted file mode 100644
index 540aea4252..0000000000
--- a/indra/llcommon/llversionviewer.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * @file llversionviewer.h
- * @brief
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLVERSIONVIEWER_H
-#define LL_LLVERSIONVIEWER_H
-
-const S32 LL_VERSION_MAJOR = 2;
-const S32 LL_VERSION_MINOR = 0;
-const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 200030;
-
-const char * const LL_CHANNEL = "Second Life Developer";
-
-#endif
diff --git a/indra/llcommon/llwin32headers.h b/indra/llcommon/llwin32headers.h
new file mode 100644
index 0000000000..8cfa40ada8
--- /dev/null
+++ b/indra/llcommon/llwin32headers.h
@@ -0,0 +1,42 @@
+/**
+ * @file llwin32headers.h
+ * @brief sanitized include of windows header files
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWINDOWS_H
+#define LL_LLWINDOWS_H
+
+#ifdef LL_WINDOWS
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#undef WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+// reset to default, which is lean
+#define WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+#endif
+
+#endif
diff --git a/indra/llcommon/llwin32headerslean.h b/indra/llcommon/llwin32headerslean.h
new file mode 100644
index 0000000000..314e7a85d1
--- /dev/null
+++ b/indra/llcommon/llwin32headerslean.h
@@ -0,0 +1,40 @@
+/**
+ * @file llwin32headerslean.h
+ * @brief sanitized include of windows header files
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWINDOWS_H
+#define LL_LLWINDOWS_H
+
+#ifdef LL_WINDOWS
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+#undef NOMINMAX
+#endif
+
+#endif
diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp
index 2629237f7e..4c197dc1d6 100644..100755
--- a/indra/llcommon/llworkerthread.cpp
+++ b/indra/llcommon/llworkerthread.cpp
@@ -1,31 +1,25 @@
/**
* @file llworkerthread.cpp
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -40,8 +34,8 @@
//============================================================================
// Run on MAIN thread
-LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) :
- LLQueuedThread(name, threaded)
+LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded, bool should_pause) :
+ LLQueuedThread(name, threaded, should_pause)
{
mDeleteMutex = new LLMutex(NULL);
@@ -56,8 +50,8 @@ LLWorkerThread::~LLWorkerThread()
// Delete any workers in the delete queue (should be safe - had better be!)
if (!mDeleteList.empty())
{
- llwarns << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
- << " entries in delete list." << llendl;
+ LL_WARNS() << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
+ << " entries in delete list." << LL_ENDL;
}
delete mDeleteMutex;
@@ -71,8 +65,8 @@ void LLWorkerThread::clearDeleteList()
// Delete any workers in the delete queue (should be safe - had better be!)
if (!mDeleteList.empty())
{
- llwarns << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
- << " entries in delete list." << llendl;
+ LL_WARNS() << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
+ << " entries in delete list." << LL_ENDL;
mDeleteMutex->lock();
for (delete_list_t::iterator iter = mDeleteList.begin(); iter != mDeleteList.end(); ++iter)
@@ -87,7 +81,7 @@ void LLWorkerThread::clearDeleteList()
}
// virtual
-S32 LLWorkerThread::update(U32 max_time_ms)
+S32 LLWorkerThread::update(F32 max_time_ms)
{
S32 res = LLQueuedThread::update(max_time_ms);
// Delete scheduled workers
@@ -148,7 +142,7 @@ LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workercla
bool res = addRequest(req);
if (!res)
{
- llerrs << "add called after LLWorkerThread::cleanupClass()" << llendl;
+ LL_ERRS() << "add called after LLWorkerThread::cleanupClass()" << LL_ENDL;
req->deleteRequest();
handle = nullHandle();
}
@@ -215,7 +209,7 @@ LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& na
{
if (!mWorkerThread)
{
- llerrs << "LLWorkerClass() called with NULL workerthread: " << name << llendl;
+ LL_ERRS() << "LLWorkerClass() called with NULL workerthread: " << name << LL_ENDL;
}
}
@@ -229,12 +223,12 @@ LLWorkerClass::~LLWorkerClass()
LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
if (!workreq)
{
- llerrs << "LLWorkerClass destroyed with stale work handle" << llendl;
+ LL_ERRS() << "LLWorkerClass destroyed with stale work handle" << LL_ENDL;
}
if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
{
- llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl;
+ LL_ERRS() << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << LL_ENDL;
}
}
}
@@ -244,7 +238,7 @@ void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread)
mMutex.lock();
if (mRequestHandle != LLWorkerThread::nullHandle())
{
- llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl;
+ LL_ERRS() << "LLWorkerClass attempt to change WorkerThread with active worker!" << LL_ENDL;
}
mWorkerThread = workerthread;
mMutex.unlock();
@@ -304,10 +298,10 @@ void LLWorkerClass::addWork(S32 param, U32 priority)
llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK)));
if (mRequestHandle != LLWorkerThread::nullHandle())
{
- llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl;
+ LL_ERRS() << "LLWorkerClass attempt to add work with active worker!" << LL_ENDL;
}
#if _DEBUG
-// llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl;
+// LL_INFOS() << "addWork: " << mWorkerClassName << " Param: " << param << LL_ENDL;
#endif
startWork(param);
clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED);
@@ -322,7 +316,7 @@ void LLWorkerClass::abortWork(bool autocomplete)
#if _DEBUG
// LLWorkerThread::WorkRequest* workreq = mWorkerThread->getRequest(mRequestHandle);
// if (workreq)
-// llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
+// LL_INFOS() << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << LL_ENDL;
#endif
if (mRequestHandle != LLWorkerThread::nullHandle())
{
@@ -404,7 +398,7 @@ void LLWorkerClass::scheduleDelete()
void LLWorkerClass::setPriority(U32 priority)
{
mMutex.lock();
- if (mRequestHandle != LLWorkerThread::nullHandle())
+ if (mRequestHandle != LLWorkerThread::nullHandle() && mRequestPriority != priority)
{
mRequestPriority = priority;
mWorkerThread->setPriority(mRequestHandle, priority);
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
index 1756ebab6b..09776816a8 100644..100755
--- a/indra/llcommon/llworkerthread.h
+++ b/indra/llcommon/llworkerthread.h
@@ -1,41 +1,36 @@
/**
* @file llworkerthread.h
*
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_LLWORKERTHREAD_H
#define LL_LLWORKERTHREAD_H
-#include <queue>
-#include <string>
+#include <list>
#include <map>
+#include <queue>
#include <set>
+#include <string>
#include "llqueuedthread.h"
#include "llapr.h"
@@ -89,10 +84,10 @@ private:
LLMutex* mDeleteMutex;
public:
- LLWorkerThread(const std::string& name, bool threaded = true);
+ LLWorkerThread(const std::string& name, bool threaded = true, bool should_pause = false);
~LLWorkerThread();
- /*virtual*/ S32 update(U32 max_time_ms);
+ /*virtual*/ S32 update(F32 max_time_ms);
handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL);
diff --git a/indra/llcommon/metaclass.cpp b/indra/llcommon/metaclass.cpp
deleted file mode 100644
index 90679d333e..0000000000
--- a/indra/llcommon/metaclass.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/**
- * @file metaclass.cpp
- * @author Babbage
- * @date 2006-05-15
- * @brief Implementation of LLMetaClass
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#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
deleted file mode 100644
index f38bcd2d57..0000000000
--- a/indra/llcommon/metaclass.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * @file metaclass.h
- * @author Babbage
- * @date 2006-05-15
- * @brief Reflective meta information describing a class.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_METACLASS_H
-#define LL_METACLASS_H
-
-#include <string>
-#include <map>
-
-#include "stdtypes.h"
-
-class LLReflective;
-class LLMetaProperty;
-class LLMetaMethod;
-class LL_COMMON_API 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
deleted file mode 100644
index 0d6a450646..0000000000
--- a/indra/llcommon/metaclasst.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * @file metaclasst.h
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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
deleted file mode 100644
index 784a52aeb5..0000000000
--- a/indra/llcommon/metaproperty.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * @file metaproperty.cpp
- * @author Babbage
- * @date 2006-05-15
- * @brief Implementation of LLMetaProperty.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#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
deleted file mode 100644
index 6c016c56dd..0000000000
--- a/indra/llcommon/metaproperty.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * @file metaproperty.h
- * @author Babbage
- * @date 2006-05-15
- * @brief Reflective meta information describing a property of a class.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_METAPROPERTY_H
-#define LL_METAPROPERTY_H
-
-#include "stdtypes.h"
-#include "llsd.h"
-#include "reflective.h"
-
-class LLMetaClass;
-class LLReflective;
-class LL_COMMON_API 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
deleted file mode 100644
index 5ad230d1d5..0000000000
--- a/indra/llcommon/metapropertyt.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/**
- * @file metapropertyt.h
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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<LLUUID>::get(const LLReflective* object) const
-{
- checkObjectClass(object);
- return NULL;
-}
-
-template <>
-inline const LLReflective* LLMetaPropertyT<bool>::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<LLUUID>::getLLSD(const LLReflective* object) const
-{
- return *(getProperty(object));
-}
-
-template <>
-inline LLSD LLMetaPropertyT<bool>::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
deleted file mode 100644
index 88e07d789c..0000000000
--- a/indra/llcommon/reflective.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * @file reflective.cpp
- * @author Babbage
- * @date 2006-05-15
- * @brief Implementation of LLReflective.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "linden_common.h"
-
-#include "reflective.h"
-
-LLReflective::LLReflective()
-{
-}
-
-//virtual
-LLReflective::~LLReflective()
-{
-}
diff --git a/indra/llcommon/reflective.h b/indra/llcommon/reflective.h
deleted file mode 100644
index a13537681d..0000000000
--- a/indra/llcommon/reflective.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * @file reflective.h
- * @author Babbage
- * @date 2006-05-15
- * @brief Interface that must be implemented by all reflective classes.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_REFLECTIVE_H
-#define LL_REFLECTIVE_H
-
-class LLMetaClass;
-class LL_COMMON_API 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
deleted file mode 100644
index b3195cb257..0000000000
--- a/indra/llcommon/reflectivet.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * @file reflectivet.h
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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
deleted file mode 100644
index 57f2418c0f..0000000000
--- a/indra/llcommon/roles_constants.h
+++ /dev/null
@@ -1,197 +0,0 @@
-/**
- * @file roles_constants.h
- * @brief General Roles Constants
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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: use these for any single bit powers you need
-// bit 0x1 << 41
-// bit 0x1 << 46
-// bit 0x1 << 49 and above
-
-// These powers were removed to make group roles simpler
-// 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_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
-const U64 GP_LAND_RETURN_GROUP_OWNED= 0x1LL << 48; // Return objects on parcel that are owned by the 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
-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
-// TODO: _DEPRECATED suffix as part of vote removal - DEV-24856:
-const U64 GP_PROPOSAL_START = 0x1LL << 44; // Start Proposal
-// TODO: _DEPRECATED suffix as part of vote removal - DEV-24856:
-const U64 GP_PROPOSAL_VOTE = 0x1LL << 45; // Vote on Proposal
-
-// Group chat moderation related
-const U64 GP_SESSION_JOIN = 0x1LL << 16; //can join session
-const U64 GP_SESSION_VOICE = 0x1LL << 27; //can hear/talk
-const U64 GP_SESSION_MODERATOR = 0x1LL << 37; //can mute people's session
-
-const U64 GP_DEFAULT_MEMBER = GP_ACCOUNTING_ACCOUNTABLE
- | GP_LAND_ALLOW_SET_HOME
- | GP_NOTICES_RECEIVE
- | GP_SESSION_JOIN
- | GP_SESSION_VOICE
- ;
-
-const U64 GP_DEFAULT_OFFICER = GP_DEFAULT_MEMBER // Superset of GP_DEFAULT_MEMBER
- | 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_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_SEND
- | GP_OBJECT_DEED
- | GP_OBJECT_MANIPULATE
- | GP_OBJECT_SET_SALE
- | GP_ROLE_ASSIGN_MEMBER_LIMITED
- | GP_ROLE_PROPERTIES
- | GP_SESSION_MODERATOR
- ;
-#endif
diff --git a/indra/llcommon/stdenums.h b/indra/llcommon/stdenums.h
deleted file mode 100644
index 1a5678dde1..0000000000
--- a/indra/llcommon/stdenums.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/**
- * @file stdenums.h
- * @brief Enumerations for indra.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_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_LINK = 14,
- DAD_COUNT = 15, // 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
index af0b4dd4ed..bf3f3f9ee8 100644..100755
--- a/indra/llcommon/stdtypes.h
+++ b/indra/llcommon/stdtypes.h
@@ -2,31 +2,25 @@
* @file stdtypes.h
* @brief Basic type declarations for cross platform compatibility.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#ifndef LL_STDTYPES_H
diff --git a/indra/llcommon/string_table.h b/indra/llcommon/string_table.h
index 176be21e6f..fe6416fb50 100644
--- a/indra/llcommon/string_table.h
+++ b/indra/llcommon/string_table.h
@@ -2,31 +2,25 @@
* @file string_table.h
* @brief Legacy wrapper header.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "llstringtable.h"
diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h
index 6399547f5e..a5a90d7297 100644..100755
--- a/indra/llcommon/stringize.h
+++ b/indra/llcommon/stringize.h
@@ -4,8 +4,25 @@
* @date 2008-12-17
* @brief stringize(item) template function and STRINGIZE(expression) macro
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -13,22 +30,65 @@
#define LL_STRINGIZE_H
#include <sstream>
-#include <boost/lambda/lambda.hpp>
+#include <boost/phoenix/phoenix.hpp>
+#include <llstring.h>
/**
- * stringize(item) encapsulates an idiom we use constantly, using
+ * gstringize(item) encapsulates an idiom we use constantly, using
* operator<<(std::ostringstream&, TYPE) followed by std::ostringstream::str()
+ * or their wstring equivalents
* to render a string expressing some item.
*/
-template <typename T>
-std::string stringize(const T& item)
+template <typename CHARTYPE, typename T>
+std::basic_string<CHARTYPE> gstringize(const T& item)
{
- std::ostringstream out;
+ std::basic_ostringstream<CHARTYPE> out;
out << item;
return out.str();
}
/**
+ *partial specialization of stringize for handling wstring
+ *TODO: we should have similar specializations for wchar_t[] but not until it is needed.
+ */
+inline std::string stringize(const std::wstring& item)
+{
+ LL_WARNS() << "WARNING: Possible narrowing" << LL_ENDL;
+
+ std::string s;
+
+ s = wstring_to_utf8str(item);
+ return gstringize<char>(s);
+}
+
+/**
+ * Specialization of gstringize for std::string return types
+ */
+template <typename T>
+std::string stringize(const T& item)
+{
+ return gstringize<char>(item);
+}
+
+/**
+ * Specialization for generating wstring from string.
+ * Both a convenience function and saves a miniscule amount of overhead.
+ */
+inline std::wstring wstringize(const std::string& item)
+{
+ return gstringize<wchar_t>(item.c_str());
+}
+
+/**
+ * Specialization of gstringize for std::wstring return types
+ */
+template <typename T>
+std::wstring wstringize(const T& item)
+{
+ return gstringize<wchar_t>(item);
+}
+
+/**
* stringize_f(functor)
*/
template <typename Functor>
@@ -48,7 +108,7 @@ std::string stringize_f(Functor const & f)
* return out.str();
* @endcode
*/
-#define STRINGIZE(EXPRESSION) (stringize_f(boost::lambda::_1 << EXPRESSION))
+#define STRINGIZE(EXPRESSION) (stringize_f(boost::phoenix::placeholders::arg1 << EXPRESSION))
/**
@@ -84,7 +144,7 @@ void destringize_f(std::string const & str, Functor const & f)
* in >> item1 >> item2 >> item3 ... ;
* @endcode
*/
-#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::lambda::_1 >> EXPRESSION)))
+#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::phoenix::placeholders::arg1 >> EXPRESSION)))
#endif /* ! defined(LL_STRINGIZE_H) */
diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h
new file mode 100755
index 0000000000..a380b00a05
--- /dev/null
+++ b/indra/llcommon/tests/StringVec.h
@@ -0,0 +1,37 @@
+/**
+ * @file StringVec.h
+ * @author Nat Goodspeed
+ * @date 2012-02-24
+ * @brief Extend TUT ensure_equals() to handle std::vector<std::string>
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_STRINGVEC_H)
+#define LL_STRINGVEC_H
+
+#include <vector>
+#include <string>
+#include <iostream>
+
+typedef std::vector<std::string> StringVec;
+
+std::ostream& operator<<(std::ostream& out, const StringVec& strings)
+{
+ out << '(';
+ StringVec::const_iterator begin(strings.begin()), end(strings.end());
+ if (begin != end)
+ {
+ out << '"' << *begin << '"';
+ while (++begin != end)
+ {
+ out << ", \"" << *begin << '"';
+ }
+ }
+ out << ')';
+ return out;
+}
+
+#endif /* ! defined(LL_STRINGVEC_H) */
diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp
index 09fd037f02..9bfd567068 100644..100755
--- a/indra/llcommon/tests/bitpack_test.cpp
+++ b/indra/llcommon/tests/bitpack_test.cpp
@@ -4,37 +4,31 @@
* @date 2007-02
* @brief llstreamtools test cases.
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
-#include "../bitpack.h"
+#include "../llbitpack.h"
#include "../test/lltut.h"
@@ -46,7 +40,7 @@ namespace tut
};
typedef test_group<bit_pack> bit_pack_t;
typedef bit_pack_t::object bit_pack_object_t;
- tut::bit_pack_t tut_bit_pack("bitpack");
+ tut::bit_pack_t tut_bit_pack("LLBitPack");
// pack -> unpack
template<> template<>
@@ -77,7 +71,6 @@ namespace tut
U8 packbuffer[255];
U8 unpackbuffer[255];
int pack_bufsize = 0;
- int unpack_bufsize = 0;
LLBitPack bitpack(packbuffer, 255);
@@ -87,19 +80,19 @@ namespace tut
pack_bufsize = bitpack.flushBitPack();
LLBitPack bitunpack(packbuffer, pack_bufsize*8);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]);
- unpack_bufsize = bitunpack.bitUnpack(&unpackbuffer[0], 8);
+ bitunpack.bitUnpack(&unpackbuffer[0], 8);
ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]);
- unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
+ bitunpack.bitUnpack(unpackbuffer, 8*4); // Life
ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4);
}
diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp
index ca27fe9b23..4b3e07fa75 100644..100755
--- a/indra/llcommon/tests/commonmisc_test.cpp
+++ b/indra/llcommon/tests/commonmisc_test.cpp
@@ -4,31 +4,25 @@
* @date 2005-10-12
* @brief Common templates for test framework
*
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -65,7 +59,7 @@ namespace tut
};
typedef test_group<sd_data> sd_test;
typedef sd_test::object sd_object;
- tut::sd_test sd("llsd");
+ tut::sd_test sd("LLSD");
template<> template<>
void sd_object::test<1>()
@@ -345,7 +339,7 @@ namespace tut
/*
if(actual != expected)
{
- llwarns << "iteration " << i << llendl;
+ LL_WARNS() << "iteration " << i << LL_ENDL;
std::ostringstream e_str;
std::string::iterator iter = expected.begin();
std::string::iterator end = expected.end();
@@ -355,8 +349,8 @@ namespace tut
}
e_str << std::endl;
llsd_serialize_string(e_str, expected);
- llwarns << "expected size: " << expected.size() << llendl;
- llwarns << "expected: " << e_str.str() << llendl;
+ LL_WARNS() << "expected size: " << expected.size() << LL_ENDL;
+ LL_WARNS() << "expected: " << e_str.str() << LL_ENDL;
std::ostringstream a_str;
iter = actual.begin();
@@ -367,8 +361,8 @@ namespace tut
}
a_str << std::endl;
llsd_serialize_string(a_str, actual);
- llwarns << "actual size: " << actual.size() << llendl;
- llwarns << "actual: " << a_str.str() << llendl;
+ LL_WARNS() << "actual size: " << actual.size() << LL_ENDL;
+ LL_WARNS() << "actual: " << a_str.str() << LL_ENDL;
}
*/
ensure_equals("string value", actual, expected);
@@ -456,7 +450,7 @@ namespace tut
};
typedef test_group<mem_data> mem_test;
typedef mem_test::object mem_object;
- tut::mem_test mem_stream("memory_stream");
+ tut::mem_test mem_stream("LLMemoryStream");
template<> template<>
void mem_object::test<1>()
@@ -649,7 +643,7 @@ namespace tut
};
typedef test_group<hash_data> hash_test;
typedef hash_test::object hash_object;
- tut::hash_test hash_tester("hash_test");
+ tut::hash_test hash_tester("LLHash");
template<> template<>
void hash_object::test<1>()
diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h
index fa12f944ef..9c5c18a150 100644..100755
--- a/indra/llcommon/tests/listener.h
+++ b/indra/llcommon/tests/listener.h
@@ -4,8 +4,25 @@
* @date 2009-03-06
* @brief Useful for tests of the LLEventPump family of classes
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -13,6 +30,8 @@
#define LL_LISTENER_H
#include "llsd.h"
+#include "llevents.h"
+#include "tests/StringVec.h"
#include <iostream>
/*****************************************************************************
@@ -116,24 +135,7 @@ struct Collect
return false;
}
void clear() { result.clear(); }
- typedef std::vector<std::string> StringList;
- StringList result;
+ StringVec result;
};
-std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings)
-{
- out << '(';
- Collect::StringList::const_iterator begin(strings.begin()), end(strings.end());
- if (begin != end)
- {
- out << '"' << *begin << '"';
- while (++begin != end)
- {
- out << ", \"" << *begin << '"';
- }
- }
- out << ')';
- return out;
-}
-
#endif /* ! defined(LL_LISTENER_H) */
diff --git a/indra/llcommon/tests/llallocator_heap_profile_test.cpp b/indra/llcommon/tests/llallocator_heap_profile_test.cpp
index 7369fdc8bc..44a9705803 100644..100755
--- a/indra/llcommon/tests/llallocator_heap_profile_test.cpp
+++ b/indra/llcommon/tests/llallocator_heap_profile_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-02-
* @brief Test for llallocator_heap_profile.cpp.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llallocator_test.cpp b/indra/llcommon/tests/llallocator_test.cpp
index 9db95f4273..4e62eaee67 100644..100755
--- a/indra/llcommon/tests/llallocator_test.cpp
+++ b/indra/llcommon/tests/llallocator_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-02-
* @brief Test for llallocator.cpp.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llbase64_test.cpp b/indra/llcommon/tests/llbase64_test.cpp
index 6009788b22..d0394150fa 100644..100755
--- a/indra/llcommon/tests/llbase64_test.cpp
+++ b/indra/llcommon/tests/llbase64_test.cpp
@@ -3,31 +3,25 @@
* @author James Cook
* @date 2007-02-04
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -47,7 +41,7 @@ namespace tut
};
typedef test_group<base64_data> base64_test;
typedef base64_test::object base64_object;
- tut::base64_test base64("base64");
+ tut::base64_test base64("LLBase64");
template<> template<>
void base64_object::test<1>()
diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp
index c31259227a..7c95ccb91f 100644..100755
--- a/indra/llcommon/tests/lldate_test.cpp
+++ b/indra/llcommon/tests/lldate_test.cpp
@@ -4,31 +4,25 @@
* @date 2007-02
* @brief LLDate test cases.
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -77,7 +71,7 @@ namespace tut
};
typedef test_group<date_test> date_test_t;
typedef date_test_t::object date_test_object_t;
- tut::date_test_t tut_date_test("date_test");
+ tut::date_test_t tut_date_test("LLDate");
/* format validation */
template<> template<>
diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp
new file mode 100644
index 0000000000..23167762c3
--- /dev/null
+++ b/indra/llcommon/tests/lldeadmantimer_test.cpp
@@ -0,0 +1,628 @@
+/**
+ * @file lldeadmantimer_test.cpp
+ * @brief Tests for the LLDeadmanTimer class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../lldeadmantimer.h"
+#include "../llsd.h"
+#include "../lltimer.h"
+
+#include "../test/lltut.h"
+
+// Convert between floating point time deltas and U64 time deltas.
+// Reflects an implementation detail inside lldeadmantimer.cpp
+
+static LLDeadmanTimer::time_type float_time_to_u64(F64 delta)
+{
+ return LLDeadmanTimer::time_type(delta * get_timer_info().mClockFrequency);
+}
+
+static F64 u64_time_to_float(LLDeadmanTimer::time_type delta)
+{
+ return delta * get_timer_info().mClockFrequencyInv;
+}
+
+
+namespace tut
+{
+
+struct deadmantimer_test
+{
+ deadmantimer_test()
+ {
+ // LLTimer internals updating
+ get_timer_info().update();
+ }
+};
+
+typedef test_group<deadmantimer_test> deadmantimer_group_t;
+typedef deadmantimer_group_t::object deadmantimer_object_t;
+tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer");
+
+// Basic construction test and isExpired() call
+template<> template<>
+void deadmantimer_object_t::test<1>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8));
+ }
+
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+ ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// Construct with zero horizon - not useful generally but will be useful in testing
+template<> template<>
+void deadmantimer_object_t::test<2>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(0.0, false); // Zero is pre-expired
+
+ ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()",
+ timer.isExpired(0, started, stopped, count), false);
+ }
+
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(0.0, true); // Zero is pre-expired
+
+ ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+ }
+}
+
+
+// "pre-expired" timer - starting a timer with a 0.0 horizon will result in
+// expiration on first test.
+template<> template<>
+void deadmantimer_object_t::test<3>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(0.0, false);
+
+ timer.start(0);
+ ensure_equals("WOCM isExpired() returns true with 0.0 horizon time",
+ timer.isExpired(0, started, stopped, count), true);
+ ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(0.0, true);
+
+ timer.start(0);
+ ensure_equals("WCM isExpired() returns true with 0.0 horizon time",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8);
+ }
+}
+
+
+// "pre-expired" timer - bell rings are ignored as we're already expired.
+template<> template<>
+void deadmantimer_object_t::test<4>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(0.0, false);
+
+ timer.start(0);
+ timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+ ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring",
+ timer.isExpired(0, started, stopped, count), true);
+ ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(0.0, true);
+
+ timer.start(0);
+ timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1);
+ ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8);
+ }
+}
+
+
+// start(0) test - unexpired timer reports unexpired
+template<> template<>
+void deadmantimer_object_t::test<5>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ timer.start(0);
+ ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time",
+ timer.isExpired(0, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8));
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ timer.start(0);
+ ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time",
+ timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false);
+ ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// start() test - start in the past but not beyond 1 horizon
+template<> template<>
+void deadmantimer_object_t::test<6>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
+ timer.start(the_past);
+ ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+ timer.isExpired(now, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8));
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0));
+ timer.start(the_past);
+ ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("t6 - isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// start() test - start in the past but well beyond 1 horizon
+template<> template<>
+void deadmantimer_object_t::test<7>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now,started, stopped, count), true);
+ ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8);
+ }
+}
+
+
+// isExpired() test - results are read-once. Probes after first true are false.
+template<> template<>
+void deadmantimer_object_t::test<8>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(10.0, false);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now, started, stopped, count), true);
+
+ started = 42.0;
+ stopped = 97.0;
+ count = U64L(8);
+ ensure_equals("WOCM t8 - second isExpired() returns false after true",
+ timer.isExpired(now, started, stopped, count), false);
+ ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(10.0, true);
+
+ // Would like to do subtraction on current time but can't because
+ // the implementation on Windows is zero-based. We wrap around
+ // the backside resulting in a large U64 number.
+
+ LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow());
+ LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0));
+ timer.start(the_past);
+ ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+
+ started = 42.0;
+ stopped = 97.0;
+ count = U64L(8);
+ user_cpu = 29000;
+ sys_cpu = 57000;
+ ensure_equals("WCM t8 - second isExpired() returns false after true",
+ timer.isExpired(now, started, stopped, count), false);
+ ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2);
+ ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2);
+ ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8));
+ ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000));
+ ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000));
+ }
+}
+
+
+// ringBell() test - see that we can keep a timer from expiring
+template<> template<>
+void deadmantimer_object_t::test<9>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(5.0, false);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count), true);
+ ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+ LLDeadmanTimer timer(5.0, true);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ }
+}
+
+
+// restart after expiration test - verify that restarts behave well
+template<> template<>
+void deadmantimer_object_t::test<10>()
+{
+ {
+ // Without cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8));
+ LLDeadmanTimer timer(5.0, false);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count), true);
+ ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false);
+
+ // Jump forward and restart
+ now += float_time_to_u64(1.0);
+ real_start = u64_time_to_float(now);
+ timer.start(now);
+
+ // Run a modified bell ring sequence
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+ timer.isExpired(now, started, stopped, count), false);
+ last_good_ring = u64_time_to_float(now);
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump",
+ timer.isExpired(now, started, stopped, count), true);
+ ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8));
+ ensure_equals("WOCM t10 - single read only - 2nd start",
+ timer.isExpired(now, started, stopped, count), false);
+ }
+ {
+ // With cpu metrics
+ F64 started(42.0), stopped(97.0);
+ U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000);
+
+ LLDeadmanTimer timer(5.0, true);
+
+ LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow());
+ F64 real_start(u64_time_to_float(now));
+ timer.start(0);
+
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ F64 last_good_ring(u64_time_to_float(now));
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10));
+ ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+
+ // Jump forward and restart
+ now += float_time_to_u64(1.0);
+ real_start = u64_time_to_float(now);
+ timer.start(now);
+
+ // Run a modified bell ring sequence
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ now += float_time_to_u64(1.0);
+ timer.ringBell(now, 1);
+ ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ last_good_ring = u64_time_to_float(now);
+
+ // Jump forward and expire
+ now += float_time_to_u64(10.0);
+ ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true);
+ ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4);
+ ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4);
+ ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8));
+ ensure_equals("WCM t10 - single read only - 2nd start",
+ timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false);
+ }
+}
+
+
+
+} // end namespace tut
diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp
index f3c25de8b5..b5e189a465 100644..100755
--- a/indra/llcommon/tests/lldependencies_test.cpp
+++ b/indra/llcommon/tests/lldependencies_test.cpp
@@ -4,8 +4,25 @@
* @date 2008-09-17
* @brief Test of lldependencies.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -20,29 +37,14 @@
// associated header
#include "../lldependencies.h"
// other Linden headers
-#include "../test/lltut.h"
-
-using boost::assign::list_of;
#if LL_WINDOWS
#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
#endif
-typedef LLDependencies<> StringDeps;
-typedef StringDeps::KeyList StringList;
-
-// We use the very cool boost::assign::list_of() construct to specify vectors
-// of strings inline. For reasons on which I'm not entirely clear, though, it
-// needs a helper function. You can use list_of() to construct an implicit
-// StringList (std::vector<std::string>) by conversion, e.g. for a function
-// parameter -- but if you simply write StringList(list_of("etc.")), you get
-// ambiguity errors. Shrug!
-template<typename CONTAINER>
-CONTAINER make(const CONTAINER& data)
-{
- return data;
-}
-
+/*****************************************************************************
+* Display helpers: must be defined BEFORE lltut.h!
+*****************************************************************************/
// Display an arbitary value as itself...
template<typename T>
std::ostream& display(std::ostream& out, const T& value)
@@ -96,6 +98,31 @@ std::ostream& operator<<(std::ostream& out, const std::set<ENTRY>& set)
return out;
}
+/*****************************************************************************
+* Now we can #include lltut.h
+*****************************************************************************/
+#include "../test/lltut.h"
+
+/*****************************************************************************
+* Other helpers
+*****************************************************************************/
+using boost::assign::list_of;
+
+typedef LLDependencies<> StringDeps;
+typedef StringDeps::KeyList StringList;
+
+// We use the very cool boost::assign::list_of() construct to specify vectors
+// of strings inline. For reasons on which I'm not entirely clear, though, it
+// needs a helper function. You can use list_of() to construct an implicit
+// StringList (std::vector<std::string>) by conversion, e.g. for a function
+// parameter -- but if you simply write StringList(list_of("etc.")), you get
+// ambiguity errors. Shrug!
+template<typename CONTAINER>
+CONTAINER make(const CONTAINER& data)
+{
+ return data;
+}
+
const std::string& extract_key(const LLDependencies<>::value_type& entry)
{
return entry.first;
@@ -128,7 +155,7 @@ namespace tut
};
typedef test_group<deps_data> deps_group;
typedef deps_group::object deps_object;
- tut::deps_group depsgr("lldependencies");
+ tut::deps_group depsgr("LLDependencies");
template<> template<>
void deps_object::test<1>()
@@ -241,10 +268,10 @@ namespace tut
++const_iterator;
ensure_equals(const_iterator->first, "def");
ensure_equals(const_iterator->second, 2);
- NameIndexDeps::node_range node_range(nideps.get_node_range());
- ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3)));
- *node_range.begin() = 0;
- *node_range.begin() = 1;
+// NameIndexDeps::node_range node_range(nideps.get_node_range());
+// ensure_equals(instance_from_range<std::vector<int> >(node_range), make< std::vector<int> >(list_of(1)(2)(3)));
+// *node_range.begin() = 0;
+// *node_range.begin() = 1;
NameIndexDeps::const_node_range const_node_range(const_nideps.get_node_range());
ensure_equals(instance_from_range<std::vector<int> >(const_node_range), make< std::vector<int> >(list_of(1)(2)(3)));
NameIndexDeps::const_key_range const_key_range(const_nideps.get_key_range());
@@ -261,8 +288,8 @@ namespace tut
def);
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_range().begin())),
def);
- ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_node_range().begin())),
- def);
+// ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_node_range().begin())),
+// def);
ensure_equals(instance_from_range<StringList>(const_nideps.get_after_range(const_nideps.get_node_range().begin())),
def);
ensure_equals(instance_from_range<StringList>(nideps.get_after_range(nideps.get_key_range().begin())),
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index 6785d0cf17..f51279e817 100644..100755
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -1,33 +1,27 @@
-/**
+/**
* @file llerror_test.cpp
* @date December 2006
* @brief error unit tests
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -44,9 +38,12 @@
namespace
{
+#ifdef __clang__
+# pragma clang diagnostic ignored "-Wunused-function"
+#endif
void test_that_error_h_includes_enough_things_to_compile_a_message()
{
- llinfos << "!" << llendl;
+ LL_INFOS() << "!" << LL_ENDL;
}
}
@@ -54,24 +51,26 @@ namespace
{
static bool fatalWasCalled;
void fatalCall(const std::string&) { fatalWasCalled = true; }
-
+}
+
+namespace tut
+{
class TestRecorder : public LLError::Recorder
{
public:
- TestRecorder() : mWantsTime(false) { }
- ~TestRecorder() { LLError::removeRecorder(this); }
-
- void recordMessage(LLError::ELevel level,
- const std::string& message)
+ TestRecorder() { mWantsTime = false; }
+ virtual ~TestRecorder() { }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message)
{
mMessages.push_back(message);
}
-
+
int countMessages() { return (int) mMessages.size(); }
void clearMessages() { mMessages.clear(); }
-
+
void setWantsTime(bool t) { mWantsTime = t; }
- bool wantsTime() { return mWantsTime; }
std::string message(int n)
{
@@ -81,41 +80,57 @@ namespace
tut::ensure(test_name.str(), n < countMessages());
return mMessages[n];
}
-
+
private:
typedef std::vector<std::string> MessageVector;
MessageVector mMessages;
-
- bool mWantsTime;
};
-}
-
-namespace tut
-{
+
struct ErrorTestData
{
- TestRecorder mRecorder;
- LLError::Settings* mPriorErrorSettings;
-
- ErrorTestData()
+ LLError::RecorderPtr mRecorder;
+ LLError::SettingsStoragePtr mPriorErrorSettings;
+
+ ErrorTestData():
+ mRecorder(new TestRecorder())
{
fatalWasCalled = false;
-
+
mPriorErrorSettings = LLError::saveAndResetSettings();
LLError::setDefaultLevel(LLError::LEVEL_DEBUG);
LLError::setFatalFunction(fatalCall);
- LLError::addRecorder(&mRecorder);
+ LLError::addRecorder(mRecorder);
}
-
+
~ErrorTestData()
{
- LLError::removeRecorder(&mRecorder);
+ LLError::removeRecorder(mRecorder);
LLError::restoreSettings(mPriorErrorSettings);
}
-
+
+ int countMessages()
+ {
+ return boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->countMessages();
+ }
+
+ void clearMessages()
+ {
+ boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->clearMessages();
+ }
+
+ void setWantsTime(bool t)
+ {
+ boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->setWantsTime(t);
+ }
+
+ std::string message(int n)
+ {
+ return boost::dynamic_pointer_cast<TestRecorder>(mRecorder)->message(n);
+ }
+
void ensure_message_count(int expectedCount)
{
- ensure_equals("message count", mRecorder.countMessages(), expectedCount);
+ ensure_equals("message count", countMessages(), expectedCount);
}
void ensure_message_contains(int n, const std::string& expectedText)
@@ -123,7 +138,7 @@ namespace tut
std::ostringstream test_name;
test_name << "testing message " << n;
- ensure_contains(test_name.str(), mRecorder.message(n), expectedText);
+ ensure_contains(test_name.str(), message(n), expectedText);
}
void ensure_message_does_not_contain(int n, const std::string& expectedText)
@@ -131,22 +146,22 @@ namespace tut
std::ostringstream test_name;
test_name << "testing message " << n;
- ensure_does_not_contain(test_name.str(), mRecorder.message(n), expectedText);
+ ensure_does_not_contain(test_name.str(), message(n), expectedText);
}
};
-
+
typedef test_group<ErrorTestData> ErrorTestGroup;
typedef ErrorTestGroup::object ErrorTestObject;
-
+
ErrorTestGroup errorTestGroup("error");
-
+
template<> template<>
void ErrorTestObject::test<1>()
// basic test of output
{
- llinfos << "test" << llendl;
- llinfos << "bob" << llendl;
-
+ LL_INFOS() << "test" << LL_ENDL;
+ LL_INFOS() << "bob" << LL_ENDL;
+
ensure_message_contains(0, "test");
ensure_message_contains(1, "bob");
}
@@ -156,16 +171,16 @@ namespace
{
void writeSome()
{
- lldebugs << "one" << llendl;
- llinfos << "two" << llendl;
- llwarns << "three" << llendl;
- llerrs << "four" << llendl;
- // fatal messages write out and addtional "error" message
+ LL_DEBUGS() << "one" << LL_ENDL;
+ LL_INFOS() << "two" << LL_ENDL;
+ LL_WARNS() << "three" << LL_ENDL;
+ // fatal messages write out an additional "error" message
+ LL_ERRS() << "four" << LL_ENDL;
}
};
namespace tut
-{
+{
template<> template<>
void ErrorTestObject::test<2>()
// messages are filtered based on default level
@@ -178,7 +193,7 @@ namespace tut
ensure_message_contains(3, "error");
ensure_message_contains(4, "four");
ensure_message_count(5);
-
+
LLError::setDefaultLevel(LLError::LEVEL_INFO);
writeSome();
ensure_message_contains(5, "two");
@@ -186,20 +201,20 @@ namespace tut
ensure_message_contains(7, "error");
ensure_message_contains(8, "four");
ensure_message_count(9);
-
+
LLError::setDefaultLevel(LLError::LEVEL_WARN);
writeSome();
ensure_message_contains(9, "three");
ensure_message_contains(10, "error");
ensure_message_contains(11, "four");
ensure_message_count(12);
-
+
LLError::setDefaultLevel(LLError::LEVEL_ERROR);
writeSome();
ensure_message_contains(12, "error");
ensure_message_contains(13, "four");
ensure_message_count(14);
-
+
LLError::setDefaultLevel(LLError::LEVEL_NONE);
writeSome();
ensure_message_count(14);
@@ -224,14 +239,14 @@ namespace tut
{
std::string thisFile = __FILE__;
std::string abbreviateFile = LLError::abbreviateFile(thisFile);
-
+
ensure_ends_with("file name abbreviation",
abbreviateFile,
"llcommon/tests/llerror_test.cpp"
);
ensure_does_not_contain("file name abbreviation",
abbreviateFile, "indra");
-
+
std::string someFile =
#if LL_WINDOWS
"C:/amy/bob/cam.cpp"
@@ -240,12 +255,12 @@ namespace tut
#endif
;
std::string someAbbreviation = LLError::abbreviateFile(someFile);
-
+
ensure_equals("non-indra file abbreviation",
someAbbreviation, someFile);
}
}
-
+
namespace
{
std::string locationString(int line)
@@ -253,44 +268,45 @@ namespace
std::ostringstream location;
location << LLError::abbreviateFile(__FILE__)
<< "(" << line << ") : ";
-
+
return location.str();
}
-
+
std::string writeReturningLocation()
{
- llinfos << "apple" << llendl; int this_line = __LINE__;
+ LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__;
return locationString(this_line);
}
-
- std::string writeReturningLocationAndFunction()
+
+ void writeReturningLocationAndFunction(std::string& location, std::string& function)
{
- llinfos << "apple" << llendl; int this_line = __LINE__;
- return locationString(this_line) + __FUNCTION__;
+ LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__;
+ location = locationString(this_line);
+ function = __FUNCTION__;
}
-
+
std::string errorReturningLocation()
{
- llerrs << "die" << llendl; int this_line = __LINE__;
+ LL_ERRS() << "die" << LL_ENDL; int this_line = __LINE__;
return locationString(this_line);
}
}
namespace tut
-{
+{
template<> template<>
void ErrorTestObject::test<5>()
// file and line information in log messages
{
std::string location = writeReturningLocation();
// expecting default to not print location information
-
+
LLError::setPrintLocation(true);
writeReturningLocation();
-
+
LLError::setPrintLocation(false);
writeReturningLocation();
-
+
ensure_message_does_not_contain(0, location);
ensure_message_contains(1, location);
ensure_message_does_not_contain(2, location);
@@ -303,16 +319,16 @@ namespace tut
existing log messages often do.) The functions all return their C++
name so that test can be substantial mechanized.
*/
-
+
std::string logFromGlobal(bool id)
{
- llinfos << (id ? "logFromGlobal: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "logFromGlobal: " : "") << "hi" << LL_ENDL;
return "logFromGlobal";
}
static std::string logFromStatic(bool id)
{
- llinfos << (id ? "logFromStatic: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "logFromStatic: " : "") << "hi" << LL_ENDL;
return "logFromStatic";
}
@@ -320,7 +336,7 @@ namespace
{
std::string logFromAnon(bool id)
{
- llinfos << (id ? "logFromAnon: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "logFromAnon: " : "") << "hi" << LL_ENDL;
return "logFromAnon";
}
}
@@ -328,7 +344,7 @@ namespace
namespace Foo {
std::string logFromNamespace(bool id)
{
- llinfos << (id ? "Foo::logFromNamespace: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "Foo::logFromNamespace: " : "") << "hi" << LL_ENDL;
//return "Foo::logFromNamespace";
// there is no standard way to get the namespace name, hence
// we won't be testing for it
@@ -342,37 +358,35 @@ namespace
public:
std::string logFromMember(bool id)
{
- llinfos << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << LL_ENDL;
return "ClassWithNoLogType::logFromMember";
}
static std::string logFromStatic(bool id)
{
- llinfos << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << LL_ENDL;
return "ClassWithNoLogType::logFromStatic";
}
};
-
+
class ClassWithLogType {
LOG_CLASS(ClassWithLogType);
public:
std::string logFromMember(bool id)
{
- llinfos << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << LL_ENDL;
return "ClassWithLogType::logFromMember";
}
static std::string logFromStatic(bool id)
{
- llinfos << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << llendl;
+ LL_INFOS() << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << LL_ENDL;
return "ClassWithLogType::logFromStatic";
}
};
-
+
std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); }
- std::string logFromClassWithNoLogTypeMember(bool id) { ClassWithNoLogType c; return c.logFromMember(id); }
- std::string logFromClassWithNoLogTypeStatic(bool id) { return ClassWithNoLogType::logFromStatic(id); }
std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); }
std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); }
-
+
void ensure_has(const std::string& message,
const std::string& actual, const std::string& expected)
{
@@ -380,23 +394,23 @@ namespace
if (n1 == std::string::npos)
{
std::stringstream ss;
- ss << message << ": " << "expected to find a copy of " << expected
- << " in actual " << actual;
+ ss << message << ": " << "expected to find a copy of '" << expected
+ << "' in actual '" << actual << "'";
throw tut::failure(ss.str().c_str());
}
}
-
+
typedef std::string (*LogFromFunction)(bool);
- void testLogName(TestRecorder& recorder, LogFromFunction f,
+ void testLogName(LLError::RecorderPtr recorder, LogFromFunction f,
const std::string& class_name = "")
{
- recorder.clearMessages();
+ boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->clearMessages();
std::string name = f(false);
f(true);
-
- std::string messageWithoutName = recorder.message(0);
- std::string messageWithName = recorder.message(1);
-
+
+ std::string messageWithoutName = boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(0);
+ std::string messageWithName = boost::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(1);
+
ensure_has(name + " logged without name",
messageWithoutName, name);
ensure_has(name + " logged with name",
@@ -422,9 +436,6 @@ namespace tut
testLogName(mRecorder, logFromStatic);
testLogName(mRecorder, logFromAnon);
testLogName(mRecorder, logFromNamespace);
- //testLogName(mRecorder, logFromClassWithNoLogTypeMember, "ClassWithNoLogType");
- //testLogName(mRecorder, logFromClassWithNoLogTypeStatic, "ClassWithNoLogType");
- // XXX: figure out what the exepcted response is for these
testLogName(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType");
testLogName(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType");
}
@@ -434,27 +445,22 @@ namespace
{
std::string innerLogger()
{
- llinfos << "inside" << llendl;
+ LL_INFOS() << "inside" << LL_ENDL;
return "moo";
}
-
+
std::string outerLogger()
{
- llinfos << "outside(" << innerLogger() << ")" << llendl;
+ LL_INFOS() << "outside(" << innerLogger() << ")" << LL_ENDL;
return "bar";
}
-
- void uberLogger()
- {
- llinfos << "uber(" << outerLogger() << "," << innerLogger() << ")" << llendl;
- }
-
+
class LogWhileLogging
{
public:
void print(std::ostream& out) const
{
- llinfos << "logging" << llendl;
+ LL_INFOS() << "logging" << LL_ENDL;
out << "baz";
}
};
@@ -465,13 +471,13 @@ namespace
void metaLogger()
{
LogWhileLogging l;
- llinfos << "meta(" << l << ")" << llendl;
+ LL_INFOS() << "meta(" << l << ")" << LL_ENDL;
}
-
+
}
namespace tut
-{
+{
template<> template<>
// handle nested logging
void ErrorTestObject::test<7>()
@@ -480,31 +486,24 @@ namespace tut
ensure_message_contains(0, "inside");
ensure_message_contains(1, "outside(moo)");
ensure_message_count(2);
-
- uberLogger();
- ensure_message_contains(2, "inside");
- ensure_message_contains(3, "inside");
- ensure_message_contains(4, "outside(moo)");
- ensure_message_contains(5, "uber(bar,moo)");
- ensure_message_count(6);
-
+
metaLogger();
- ensure_message_contains(6, "logging");
- ensure_message_contains(7, "meta(baz)");
- ensure_message_count(8);
+ ensure_message_contains(2, "logging");
+ ensure_message_contains(3, "meta(baz)");
+ ensure_message_count(4);
}
-
+
template<> template<>
- // special handling of llerrs calls
+ // special handling of LL_ERRS() calls
void ErrorTestObject::test<8>()
{
LLError::setPrintLocation(false);
std::string location = errorReturningLocation();
-
+
ensure_message_contains(0, location + "error");
ensure_message_contains(1, "die");
ensure_message_count(2);
-
+
ensure("fatal callback called", fatalWasCalled);
}
}
@@ -515,83 +514,79 @@ namespace
{
return "1947-07-08T03:04:05Z";
}
-
+
void ufoSighting()
{
- llinfos << "ufo" << llendl;
+ LL_INFOS() << "ufo" << LL_ENDL;
}
}
namespace tut
-{
+{
template<> template<>
// time in output (for recorders that need it)
void ErrorTestObject::test<9>()
{
LLError::setTimeFunction(roswell);
- mRecorder.setWantsTime(false);
+ setWantsTime(false);
ufoSighting();
ensure_message_contains(0, "ufo");
ensure_message_does_not_contain(0, roswell());
-
- mRecorder.setWantsTime(true);
+
+ setWantsTime(true);
ufoSighting();
ensure_message_contains(1, "ufo");
ensure_message_contains(1, roswell());
}
-
+
template<> template<>
// output order
void ErrorTestObject::test<10>()
{
-#if LL_LINUX
- skip("Fails on Linux, see comments");
-// on Linux:
-// [error, 10] fail: 'order is time type location function message: expected
-// '1947-07-08T03:04:05Z INFO: llcommon/tests/llerror_test.cpp(268) :
-// writeReturningLocationAndFunction: apple' actual
-// '1947-07-08T03:04:05Z INFO: llcommon/tests/llerror_test.cpp(268) :
-// LLError::NoClassInfo::writeReturningLocationAndFunction: apple''
-#endif
LLError::setPrintLocation(true);
LLError::setTimeFunction(roswell);
- mRecorder.setWantsTime(true);
- std::string locationAndFunction = writeReturningLocationAndFunction();
-
- ensure_equals("order is time type location function message",
- mRecorder.message(0),
- roswell() + " INFO: " + locationAndFunction + ": apple");
+ setWantsTime(true);
+ std::string location,
+ function;
+ writeReturningLocationAndFunction(location, function);
+
+ ensure_equals("order is location time type function message",
+ message(0),
+ location + roswell() + " INFO: " + function + ": apple");
}
template<> template<>
// multiple recorders
void ErrorTestObject::test<11>()
{
- TestRecorder altRecorder;
- LLError::addRecorder(&altRecorder);
-
- llinfos << "boo" << llendl;
+ LLError::RecorderPtr altRecorder(new TestRecorder());
+ LLError::addRecorder(altRecorder);
+
+ LL_INFOS() << "boo" << LL_ENDL;
ensure_message_contains(0, "boo");
- ensure_equals("alt recorder count", altRecorder.countMessages(), 1);
- ensure_contains("alt recorder message 0", altRecorder.message(0), "boo");
-
+ ensure_equals("alt recorder count", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 1);
+ ensure_contains("alt recorder message 0", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(0), "boo");
+
LLError::setTimeFunction(roswell);
- TestRecorder anotherRecorder;
- anotherRecorder.setWantsTime(true);
- LLError::addRecorder(&anotherRecorder);
-
- llinfos << "baz" << llendl;
+ LLError::RecorderPtr anotherRecorder(new TestRecorder());
+ boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->setWantsTime(true);
+ LLError::addRecorder(anotherRecorder);
+
+ LL_INFOS() << "baz" << LL_ENDL;
std::string when = roswell();
-
+
ensure_message_does_not_contain(1, when);
- ensure_equals("alt recorder count", altRecorder.countMessages(), 2);
- ensure_does_not_contain("alt recorder message 1", altRecorder.message(1), when);
- ensure_equals("another recorder count", anotherRecorder.countMessages(), 1);
- ensure_contains("another recorder message 0", anotherRecorder.message(0), when);
+ ensure_equals("alt recorder count", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 2);
+ ensure_does_not_contain("alt recorder message 1", boost::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(1), when);
+ ensure_equals("another recorder count", boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->countMessages(), 1);
+ ensure_contains("another recorder message 0", boost::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->message(0), when);
+
+ LLError::removeRecorder(altRecorder);
+ LLError::removeRecorder(anotherRecorder);
}
}
@@ -599,10 +594,10 @@ class TestAlpha
{
LOG_CLASS(TestAlpha);
public:
- static void doDebug() { lldebugs << "add dice" << llendl; }
- static void doInfo() { llinfos << "any idea" << llendl; }
- static void doWarn() { llwarns << "aim west" << llendl; }
- static void doError() { llerrs << "ate eels" << llendl; }
+ static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; }
+ static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; }
+ static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; }
+ static void doError() { LL_ERRS() << "ate eels" << LL_ENDL; }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
@@ -610,10 +605,10 @@ class TestBeta
{
LOG_CLASS(TestBeta);
public:
- static void doDebug() { lldebugs << "bed down" << llendl; }
- static void doInfo() { llinfos << "buy iron" << llendl; }
- static void doWarn() { llwarns << "bad word" << llendl; }
- static void doError() { llerrs << "big easy" << llendl; }
+ static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; }
+ static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; }
+ static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; }
+ static void doError() { LL_ERRS() << "big easy" << LL_ENDL; }
static void doAll() { doDebug(); doInfo(); doWarn(); doError(); }
};
@@ -625,10 +620,10 @@ namespace tut
{
LLError::setDefaultLevel(LLError::LEVEL_WARN);
LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO);
-
+
TestAlpha::doAll();
TestBeta::doAll();
-
+
ensure_message_contains(0, "aim west");
ensure_message_contains(1, "error");
ensure_message_contains(2, "ate eels");
@@ -638,7 +633,7 @@ namespace tut
ensure_message_contains(6, "big easy");
ensure_message_count(7);
}
-
+
template<> template<>
// filtering by function, and that it will override class filtering
void ErrorTestObject::test<13>()
@@ -647,13 +642,13 @@ namespace tut
LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN);
LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG);
LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE);
-
+
TestBeta::doAll();
ensure_message_contains(0, "buy iron");
ensure_message_contains(1, "bad word");
ensure_message_count(2);
}
-
+
template<> template<>
// filtering by file
// and that it is overridden by both class and function filtering
@@ -667,7 +662,7 @@ namespace tut
LLError::LEVEL_NONE);
LLError::setFunctionLevel("TestBeta::doError",
LLError::LEVEL_NONE);
-
+
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(0, "any idea");
@@ -675,7 +670,7 @@ namespace tut
ensure_message_contains(2, "bad word");
ensure_message_count(3);
}
-
+
template<> template<>
// proper cached, efficient lookup of filtering
void ErrorTestObject::test<15>()
@@ -705,7 +700,7 @@ namespace tut
ensure_message_count(2);
ensure_equals("sixth check", LLError::shouldLogCallCount(), 3);
}
-
+
template<> template<>
// configuration from LLSD
void ErrorTestObject::test<16>()
@@ -714,26 +709,26 @@ namespace tut
LLSD config;
config["print-location"] = true;
config["default-level"] = "DEBUG";
-
+
LLSD set1;
set1["level"] = "WARN";
set1["files"][0] = this_file;
-
+
LLSD set2;
set2["level"] = "INFO";
set2["classes"][0] = "TestAlpha";
-
+
LLSD set3;
set3["level"] = "NONE";
set3["functions"][0] = "TestAlpha::doError";
set3["functions"][1] = "TestBeta::doError";
-
+
config["settings"][0] = set1;
config["settings"][1] = set2;
config["settings"][2] = set3;
-
+
LLError::configure(config);
-
+
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(0, "any idea");
@@ -741,13 +736,13 @@ namespace tut
ensure_message_contains(1, "aim west");
ensure_message_contains(2, "bad word");
ensure_message_count(3);
-
+
// make sure reconfiguring works
LLSD config2;
config2["default-level"] = "WARN";
-
+
LLError::configure(config2);
-
+
TestAlpha::doAll();
TestBeta::doAll();
ensure_message_contains(3, "aim west");
@@ -759,13 +754,13 @@ namespace tut
ensure_message_contains(8, "big easy");
ensure_message_count(9);
}
-}
+}
/* Tests left:
handling of classes without LOG_CLASS
- live update of filtering from file
-
+ live update of filtering from file
+
syslog recorder
file recorder
cerr/stderr recorder
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index 3a2cda7735..2096807e53 100644..100755
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-04-22
* @brief Test for coroutine.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -47,10 +64,10 @@
// Boost.Coroutine #include is the *first* #include of the platform header.
// That means that client code must generally #include Boost.Coroutine headers
// before anything else.
-#include <boost/coroutine/coroutine.hpp>
+#include <boost/dcoroutine/coroutine.hpp>
// Normally, lleventcoro.h obviates future.hpp. We only include this because
// we implement a "by hand" test of future functionality.
-#include <boost/coroutine/future.hpp>
+#include <boost/dcoroutine/future.hpp>
#include <boost/bind.hpp>
#include <boost/range.hpp>
@@ -61,6 +78,7 @@
#include "../test/lltut.h"
#include "llsd.h"
+#include "llsdutil.h"
#include "llevents.h"
#include "tests/wrapllerrs.h"
#include "stringize.h"
@@ -70,13 +88,12 @@
/*****************************************************************************
* from the banana.cpp example program borrowed for test<1>()
*****************************************************************************/
-namespace coroutines = boost::coroutines;
+namespace coroutines = boost::dcoroutines;
using coroutines::coroutine;
template<typename Iter>
bool match(Iter first, Iter last, std::string match) {
std::string::iterator i = match.begin();
- i != match.end();
for(; (first != last) && (i != match.end()); ++i) {
if (*first != *i)
return false;
@@ -91,7 +108,7 @@ match_substring(BidirectionalIterator begin,
BidirectionalIterator end,
std::string xmatch,
BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) {
- BidirectionalIterator begin_ = begin;
+//BidirectionalIterator begin_ = begin;
for(; begin != end; ++begin)
if(match(begin, end, xmatch)) {
self.yield(begin);
@@ -105,7 +122,7 @@ typedef coroutine<std::string::iterator(void)> match_coroutine_type;
* Test helpers
*****************************************************************************/
// I suspect this will be typical of coroutines used in Linden software
-typedef boost::coroutines::coroutine<void()> coroutine_type;
+typedef boost::dcoroutines::coroutine<void()> coroutine_type;
/// Simulate an event API whose response is immediate: sent on receipt of the
/// initial request, rather than after some delay. This is the case that
@@ -156,10 +173,10 @@ namespace tut
// ... do whatever preliminary stuff must happen ...
// declare the future
- boost::coroutines::future<LLSD> future(self);
+ boost::dcoroutines::future<LLSD> future(self);
// tell the future what to wait for
LLTempBoundListener connection(
- LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::coroutines::make_callback(future))));
+ LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future))));
ensure("Not yet", ! future);
// attempting to dereference ("resolve") the future causes the calling
// coroutine to wait for it
@@ -196,7 +213,7 @@ namespace tut
BEGIN
{
result = postAndWait(self,
- LLSD().insert("value", 17), // request event
+ LLSDMap("value", 17), // request event
immediateAPI.getPump(), // requestPump
"reply1", // replyPump
"reply"); // request["reply"] = name
@@ -209,7 +226,7 @@ namespace tut
BEGIN
{
LLEventWithID pair = ::postAndWait2(self,
- LLSD().insert("value", 18),
+ LLSDMap("value", 18),
immediateAPI.getPump(),
"reply2",
"error2",
@@ -227,7 +244,7 @@ namespace tut
BEGIN
{
LLEventWithID pair = ::postAndWait2(self,
- LLSD().insert("value", 18).insert("fail", LLSD()),
+ LLSDMap("value", 18)("fail", LLSD()),
immediateAPI.getPump(),
"reply2",
"error2",
@@ -256,7 +273,7 @@ namespace tut
BEGIN
{
LLCoroEventPump waiter;
- result = waiter.postAndWait(self, LLSD().insert("value", 17),
+ result = waiter.postAndWait(self, LLSDMap("value", 17),
immediateAPI.getPump(), "reply");
}
END
@@ -328,13 +345,13 @@ namespace tut
LLCoroEventPumps waiter;
replyName = waiter.getName0();
errorName = waiter.getName1();
- WrapLL_ERRS capture;
+ WrapLLErrs capture;
try
{
result = waiter.waitWithLog(self);
debug("no exception");
}
- catch (const WrapLL_ERRS::FatalException& e)
+ catch (const WrapLLErrs::FatalException& e)
{
debug(STRINGIZE("exception " << e.what()));
threw = e.what();
@@ -348,7 +365,7 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- LLEventWithID pair(waiter.postAndWait(self, LLSD().insert("value", 23),
+ LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23),
immediateAPI.getPump(), "reply", "error"));
result = pair.first;
which = pair.second;
@@ -362,7 +379,7 @@ namespace tut
{
LLCoroEventPumps waiter;
LLEventWithID pair(
- waiter.postAndWait(self, LLSD().insert("value", 23).insert("fail", LLSD()),
+ waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()),
immediateAPI.getPump(), "reply", "error"));
result = pair.first;
which = pair.second;
@@ -375,7 +392,7 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithException(self, LLSD().insert("value", 8),
+ result = waiter.postAndWaitWithException(self, LLSDMap("value", 8),
immediateAPI.getPump(), "reply", "error");
}
END
@@ -389,7 +406,7 @@ namespace tut
try
{
result = waiter.postAndWaitWithException(self,
- LLSD().insert("value", 9).insert("fail", LLSD()),
+ LLSDMap("value", 9)("fail", LLSD()),
immediateAPI.getPump(), "reply", "error");
debug("no exception");
}
@@ -407,7 +424,7 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithLog(self, LLSD().insert("value", 30),
+ result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30),
immediateAPI.getPump(), "reply", "error");
}
END
@@ -418,15 +435,15 @@ namespace tut
BEGIN
{
LLCoroEventPumps waiter;
- WrapLL_ERRS capture;
+ WrapLLErrs capture;
try
{
result = waiter.postAndWaitWithLog(self,
- LLSD().insert("value", 31).insert("fail", LLSD()),
+ LLSDMap("value", 31)("fail", LLSD()),
immediateAPI.getPump(), "reply", "error");
debug("no exception");
}
- catch (const WrapLL_ERRS::FatalException& e)
+ catch (const WrapLLErrs::FatalException& e)
{
debug(STRINGIZE("exception " << e.what()));
threw = e.what();
@@ -779,4 +796,18 @@ namespace tut
ensure("no result", result.isUndefined());
ensure_contains("got error", threw, "32");
}
+}
+
+/*==========================================================================*|
+#include <boost/context/guarded_stack_allocator.hpp>
+
+namespace tut
+{
+ template<> template<>
+ void object::test<23>()
+ {
+ set_test_name("stacksize");
+ std::cout << "default_stacksize: " << boost::context::guarded_stack_allocator::default_stacksize() << '\n';
+ }
} // namespace tut
+|*==========================================================================*/
diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp
new file mode 100755
index 0000000000..5a4df81bf1
--- /dev/null
+++ b/indra/llcommon/tests/lleventdispatcher_test.cpp
@@ -0,0 +1,1324 @@
+/**
+ * @file lleventdispatcher_test.cpp
+ * @author Nat Goodspeed
+ * @date 2011-01-20
+ * @brief Test for lleventdispatcher.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventdispatcher.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "llsd.h"
+#include "llsdutil.h"
+#include "stringize.h"
+#include "tests/wrapllerrs.h"
+
+#include <map>
+#include <string>
+#include <stdexcept>
+
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/range.hpp>
+#include <boost/foreach.hpp>
+#define foreach BOOST_FOREACH
+
+#include <boost/lambda/lambda.hpp>
+
+#include <iostream>
+#include <iomanip>
+
+using boost::lambda::constant;
+using boost::lambda::constant_ref;
+using boost::lambda::var;
+
+using namespace llsd;
+
+/*****************************************************************************
+* Output control
+*****************************************************************************/
+#ifdef DEBUG_ON
+using std::cout;
+#else
+static std::ostringstream cout;
+#endif
+
+/*****************************************************************************
+* Example data, functions, classes
+*****************************************************************************/
+// We don't need a whole lot of different arbitrary-params methods, just (no |
+// (const LLSD&) | arbitrary) args (function | static method | non-static
+// method), where 'arbitrary' is (every LLSD datatype + (const char*)).
+// But we need to register each one under different names for the different
+// registration styles. Don't forget LLEventDispatcher subclass methods(const
+// LLSD&).
+
+// However, the number of target parameter conversions we want to try exceeds
+// boost::fusion::invoke()'s supported parameter-list size. Break out two
+// different lists.
+#define NPARAMSa bool b, int i, float f, double d, const char* cp
+#define NPARAMSb const std::string& s, const LLUUID& uuid, const LLDate& date, \
+ const LLURI& uri, const std::vector<U8>& bin
+#define NARGSa b, i, f, d, cp
+#define NARGSb s, uuid, date, uri, bin
+
+// For some registration methods we need methods on a subclass of
+// LLEventDispatcher. To simplify things, we'll use this Dispatcher subclass
+// for all our testing, including testing its own methods.
+class Dispatcher: public LLEventDispatcher
+{
+public:
+ Dispatcher(const std::string& name, const std::string& key):
+ LLEventDispatcher(name, key)
+ {}
+
+ // sensing member, mutable because we want to know when we've reached our
+ // const method too
+ mutable LLSD llsd;
+
+ void method1(const LLSD& obj) { llsd = obj; }
+ void cmethod1(const LLSD& obj) const { llsd = obj; }
+};
+
+// sensing vars, captured in a struct to make it convenient to clear them
+struct Vars
+{
+ LLSD llsd;
+ bool b;
+ int i;
+ float f;
+ double d;
+ // Capture param passed as char*. But merely storing a char* received from
+ // our caller, possibly the .c_str() from a concatenation expression,
+ // would be Bad: the pointer will be invalidated long before we can query
+ // it. We could allocate a new chunk of memory, copy the string data and
+ // point to that instead -- but hey, guess what, we already have a class
+ // that does that!
+ std::string cp;
+ std::string s;
+ LLUUID uuid;
+ LLDate date;
+ LLURI uri;
+ std::vector<U8> bin;
+
+ Vars():
+ // Only need to initialize the POD types, the rest should take care of
+ // default-constructing themselves.
+ b(false),
+ i(0),
+ f(0),
+ d(0)
+ {}
+
+ // Detect any non-default values for convenient testing
+ LLSD inspect() const
+ {
+ LLSD result;
+
+ if (llsd.isDefined())
+ result["llsd"] = llsd;
+ if (b)
+ result["b"] = b;
+ if (i)
+ result["i"] = i;
+ if (f)
+ result["f"] = f;
+ if (d)
+ result["d"] = d;
+ if (! cp.empty())
+ result["cp"] = cp;
+ if (! s.empty())
+ result["s"] = s;
+ if (uuid != LLUUID())
+ result["uuid"] = uuid;
+ if (date != LLDate())
+ result["date"] = date;
+ if (uri != LLURI())
+ result["uri"] = uri;
+ if (! bin.empty())
+ result["bin"] = bin;
+
+ return result;
+ }
+
+ /*------------- no-args (non-const, const, static) methods -------------*/
+ void method0()
+ {
+ cout << "method0()\n";
+ i = 17;
+ }
+
+ void cmethod0() const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->method0();
+ }
+
+ static void smethod0();
+
+ /*------------ Callable (non-const, const, static) methods -------------*/
+ void method1(const LLSD& obj)
+ {
+ cout << "method1(" << obj << ")\n";
+ llsd = obj;
+ }
+
+ void cmethod1(const LLSD& obj) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->method1(obj);
+ }
+
+ static void smethod1(const LLSD& obj);
+
+ /*-------- Arbitrary-params (non-const, const, static) methods ---------*/
+ void methodna(NPARAMSa)
+ {
+ // Because our const char* param cp might be NULL, and because we
+ // intend to capture the value in a std::string, have to distinguish
+ // between the NULL value and any non-NULL value. Use a convention
+ // easy for a human reader: enclose any non-NULL value in single
+ // quotes, reserving the unquoted string "NULL" to represent a NULL ptr.
+ std::string vcp;
+ if (cp == NULL)
+ vcp = "NULL";
+ else
+ vcp = std::string("'") + cp + "'";
+
+ cout << "methodna(" << b
+ << ", " << i
+ << ", " << f
+ << ", " << d
+ << ", " << vcp
+ << ")\n";
+
+ this->b = b;
+ this->i = i;
+ this->f = f;
+ this->d = d;
+ this->cp = vcp;
+ }
+
+ void methodnb(NPARAMSb)
+ {
+ std::ostringstream vbin;
+ foreach(U8 byte, bin)
+ {
+ vbin << std::hex << std::setfill('0') << std::setw(2) << unsigned(byte);
+ }
+
+ cout << "methodnb(" << "'" << s << "'"
+ << ", " << uuid
+ << ", " << date
+ << ", '" << uri << "'"
+ << ", " << vbin.str()
+ << ")\n";
+
+ this->s = s;
+ this->uuid = uuid;
+ this->date = date;
+ this->uri = uri;
+ this->bin = bin;
+ }
+
+ void cmethodna(NPARAMSa) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->methodna(NARGSa);
+ }
+
+ void cmethodnb(NPARAMSb) const
+ {
+ cout << 'c';
+ const_cast<Vars*>(this)->methodnb(NARGSb);
+ }
+
+ static void smethodna(NPARAMSa);
+ static void smethodnb(NPARAMSb);
+};
+/*------- Global Vars instance for free functions and static methods -------*/
+static Vars g;
+
+/*------------ Static Vars method implementations reference 'g' ------------*/
+void Vars::smethod0()
+{
+ cout << "smethod0() -> ";
+ g.method0();
+}
+
+void Vars::smethod1(const LLSD& obj)
+{
+ cout << "smethod1(" << obj << ") -> ";
+ g.method1(obj);
+}
+
+void Vars::smethodna(NPARAMSa)
+{
+ cout << "smethodna(...) -> ";
+ g.methodna(NARGSa);
+}
+
+void Vars::smethodnb(NPARAMSb)
+{
+ cout << "smethodnb(...) -> ";
+ g.methodnb(NARGSb);
+}
+
+/*--------------------------- Reset global Vars ----------------------------*/
+void clear()
+{
+ g = Vars();
+}
+
+/*------------------- Free functions also reference 'g' --------------------*/
+void free0()
+{
+ cout << "free0() -> ";
+ g.method0();
+}
+
+void free1(const LLSD& obj)
+{
+ cout << "free1(" << obj << ") -> ";
+ g.method1(obj);
+}
+
+void freena(NPARAMSa)
+{
+ cout << "freena(...) -> ";
+ g.methodna(NARGSa);
+}
+
+void freenb(NPARAMSb)
+{
+ cout << "freenb(...) -> ";
+ g.methodnb(NARGSb);
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct lleventdispatcher_data
+ {
+ WrapLLErrs redirect;
+ Dispatcher work;
+ Vars v;
+ std::string name, desc;
+ // Capture our own copy of all registered functions' descriptions
+ typedef std::map<std::string, std::string> DescMap;
+ DescMap descs;
+ // Capture the Vars instance on which we expect each function to operate
+ typedef std::map<std::string, Vars*> VarsMap;
+ VarsMap funcvars;
+ // Required structure for Callables with requirements
+ LLSD required;
+ // Parameter names for freena(), freenb()
+ LLSD params;
+ // Full, partial defaults arrays for params for freena(), freenb()
+ LLSD dft_array_full, dft_array_partial;
+ // Start index of partial defaults arrays
+ const LLSD::Integer partial_offset;
+ // Full, partial defaults maps for params for freena(), freenb()
+ LLSD dft_map_full, dft_map_partial;
+ // Most of the above are indexed by "a" or "b". Useful to have an
+ // array containing those strings for iterating.
+ std::vector<LLSD::String> ab;
+
+ lleventdispatcher_data():
+ work("test dispatcher", "op"),
+ // map {d=double, array=[3 elements]}
+ required(LLSDMap("d", LLSD::Real(0))("array", LLSDArray(LLSD())(LLSD())(LLSD()))),
+ // first several params are required, last couple optional
+ partial_offset(3)
+ {
+ // This object is reconstructed for every test<n> method. But
+ // clear global variables every time too.
+ ::clear();
+
+ const char* abs[] = { "a", "b" };
+ ab.assign(boost::begin(abs), boost::end(abs));
+
+ // Registration cases:
+ // - (Callable | subclass const method | subclass non-const method |
+ // non-subclass method) (with | without) required
+ // - (Free function | static method | non-static method), (no | arbitrary) params,
+ // array style
+ // - (Free function | static method | non-static method), (no | arbitrary) params,
+ // map style, (empty | partial | full) (array | map) defaults
+ // - Map-style errors:
+ // - (scalar | map) param names
+ // - defaults scalar
+ // - defaults array longer than params array
+ // - defaults map with plural unknown param names
+
+ // I hate to have to write things twice, because of having to keep
+ // them consistent. If we had variadic functions, addf() would be
+ // a variadic method, capturing the name and desc and passing them
+ // plus "everything else" to work.add(). If I could return a pair
+ // and use that pair as the first two args to work.add(), I'd do
+ // that. But the best I can do with present C++ is to set two
+ // instance variables as a side effect of addf(), and pass those
+ // variables to each work.add() call. :-P
+
+ /*------------------------- Callables --------------------------*/
+
+ // Arbitrary Callable with/out required params
+ addf("free1", "free1", &g);
+ work.add(name, desc, free1);
+ addf("free1_req", "free1", &g);
+ work.add(name, desc, free1, required);
+ // Subclass non-const method with/out required params
+ addf("Dmethod1", "method1", NULL);
+ work.add(name, desc, &Dispatcher::method1);
+ addf("Dmethod1_req", "method1", NULL);
+ work.add(name, desc, &Dispatcher::method1, required);
+ // Subclass const method with/out required params
+ addf("Dcmethod1", "cmethod1", NULL);
+ work.add(name, desc, &Dispatcher::cmethod1);
+ addf("Dcmethod1_req", "cmethod1", NULL);
+ work.add(name, desc, &Dispatcher::cmethod1, required);
+ // Non-subclass method with/out required params
+ addf("method1", "method1", &v);
+ work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1));
+ addf("method1_req", "method1", &v);
+ work.add(name, desc, boost::bind(&Vars::method1, boost::ref(v), _1), required);
+
+ /*--------------- Arbitrary params, array style ----------------*/
+
+ // (Free function | static method) with (no | arbitrary) params, array style
+ addf("free0_array", "free0", &g);
+ work.add(name, desc, free0);
+ addf("freena_array", "freena", &g);
+ work.add(name, desc, freena);
+ addf("freenb_array", "freenb", &g);
+ work.add(name, desc, freenb);
+ addf("smethod0_array", "smethod0", &g);
+ work.add(name, desc, &Vars::smethod0);
+ addf("smethodna_array", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna);
+ addf("smethodnb_array", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb);
+ // Non-static method with (no | arbitrary) params, array style
+ addf("method0_array", "method0", &v);
+ work.add(name, desc, &Vars::method0, boost::lambda::var(v));
+ addf("methodna_array", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, boost::lambda::var(v));
+ addf("methodnb_array", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, boost::lambda::var(v));
+
+ /*---------------- Arbitrary params, map style -----------------*/
+
+ // We lay out each params list as an array, also each array of
+ // default values we'll register. We'll zip these into
+ // (param=value) maps. Why not define them as maps and just
+ // extract the keys and values to arrays? Because that wouldn't
+ // give us the right params-list order.
+
+ // freena(), methodna(), cmethodna(), smethodna() all take same param list.
+ // Same for freenb() et al.
+ params = LLSDMap("a", LLSDArray("b")("i")("f")("d")("cp"))
+ ("b", LLSDArray("s")("uuid")("date")("uri")("bin"));
+ cout << "params:\n" << params << "\nparams[\"a\"]:\n" << params["a"] << "\nparams[\"b\"]:\n" << params["b"] << std::endl;
+ // default LLSD::Binary value
+ std::vector<U8> binary;
+ for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11)
+ {
+ binary.push_back(h);
+ }
+ // Full defaults arrays. We actually don't care what the LLUUID or
+ // LLDate values are, as long as they're different from the
+ // LLUUID() and LLDate() default values so inspect() will report
+ // them.
+ dft_array_full = LLSDMap("a", LLSDArray(true)(17)(3.14)(123456.78)("classic"))
+ ("b", LLSDArray("string")
+ (LLUUID::generateNewID())
+ (LLDate::now())
+ (LLURI("http://www.ietf.org/rfc/rfc3986.txt"))
+ (binary));
+ cout << "dft_array_full:\n" << dft_array_full << std::endl;
+ // Partial defaults arrays.
+ foreach(LLSD::String a, ab)
+ {
+ LLSD::Integer partition(std::min(partial_offset, dft_array_full[a].size()));
+ dft_array_partial[a] =
+ llsd_copy_array(dft_array_full[a].beginArray() + partition,
+ dft_array_full[a].endArray());
+ }
+ cout << "dft_array_partial:\n" << dft_array_partial << std::endl;
+
+ foreach(LLSD::String a, ab)
+ {
+ // Generate full defaults maps by zipping (params, dft_array_full).
+ dft_map_full[a] = zipmap(params[a], dft_array_full[a]);
+
+ // Generate partial defaults map by zipping alternate entries from
+ // (params, dft_array_full). Part of the point of using map-style
+ // defaults is to allow any subset of the target function's
+ // parameters to be optional, not just the rightmost.
+ for (LLSD::Integer ix = 0, ixend = params[a].size(); ix < ixend; ix += 2)
+ {
+ dft_map_partial[a][params[a][ix].asString()] = dft_array_full[a][ix];
+ }
+ }
+ cout << "dft_map_full:\n" << dft_map_full << "\ndft_map_partial:\n" << dft_map_partial << '\n';
+
+ // (Free function | static method) with (no | arbitrary) params,
+ // map style, no (empty array) defaults
+ addf("free0_map", "free0", &g);
+ work.add(name, desc, free0, LLSD::emptyArray());
+ addf("smethod0_map", "smethod0", &g);
+ work.add(name, desc, &Vars::smethod0, LLSD::emptyArray());
+ addf("freena_map_allreq", "freena", &g);
+ work.add(name, desc, freena, params["a"]);
+ addf("freenb_map_allreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"]);
+ addf("smethodna_map_allreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"]);
+ addf("smethodnb_map_allreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"]);
+ // Non-static method with (no | arbitrary) params, map style, no
+ // (empty array) defaults
+ addf("method0_map", "method0", &v);
+ work.add(name, desc, &Vars::method0, var(v), LLSD::emptyArray());
+ addf("methodna_map_allreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"]);
+ addf("methodnb_map_allreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"]);
+
+ // Except for the "more (array | map) defaults than params" error
+ // cases, tested separately below, the (partial | full)(array |
+ // map) defaults cases don't apply to no-params functions/methods.
+ // So eliminate free0, smethod0, method0 from the cases below.
+
+ // (Free function | static method) with arbitrary params, map
+ // style, partial (array | map) defaults
+ addf("freena_map_leftreq", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_array_partial["a"]);
+ addf("freenb_map_leftreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_array_partial["b"]);
+ addf("smethodna_map_leftreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_array_partial["a"]);
+ addf("smethodnb_map_leftreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_partial["b"]);
+ addf("freena_map_skipreq", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_map_partial["a"]);
+ addf("freenb_map_skipreq", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_map_partial["b"]);
+ addf("smethodna_map_skipreq", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_map_partial["a"]);
+ addf("smethodnb_map_skipreq", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_partial["b"]);
+ // Non-static method with arbitrary params, map style, partial
+ // (array | map) defaults
+ addf("methodna_map_leftreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_partial["a"]);
+ addf("methodnb_map_leftreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_partial["b"]);
+ addf("methodna_map_skipreq", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_partial["a"]);
+ addf("methodnb_map_skipreq", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_partial["b"]);
+
+ // (Free function | static method) with arbitrary params, map
+ // style, full (array | map) defaults
+ addf("freena_map_adft", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_array_full["a"]);
+ addf("freenb_map_adft", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_array_full["b"]);
+ addf("smethodna_map_adft", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_array_full["a"]);
+ addf("smethodnb_map_adft", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_array_full["b"]);
+ addf("freena_map_mdft", "freena", &g);
+ work.add(name, desc, freena, params["a"], dft_map_full["a"]);
+ addf("freenb_map_mdft", "freenb", &g);
+ work.add(name, desc, freenb, params["b"], dft_map_full["b"]);
+ addf("smethodna_map_mdft", "smethodna", &g);
+ work.add(name, desc, &Vars::smethodna, params["a"], dft_map_full["a"]);
+ addf("smethodnb_map_mdft", "smethodnb", &g);
+ work.add(name, desc, &Vars::smethodnb, params["b"], dft_map_full["b"]);
+ // Non-static method with arbitrary params, map style, full
+ // (array | map) defaults
+ addf("methodna_map_adft", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_array_full["a"]);
+ addf("methodnb_map_adft", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_array_full["b"]);
+ addf("methodna_map_mdft", "methodna", &v);
+ work.add(name, desc, &Vars::methodna, var(v), params["a"], dft_map_full["a"]);
+ addf("methodnb_map_mdft", "methodnb", &v);
+ work.add(name, desc, &Vars::methodnb, var(v), params["b"], dft_map_full["b"]);
+
+ // All the above are expected to succeed, and are setup for the
+ // tests to follow. Registration error cases are exercised as
+ // tests rather than as test setup.
+ }
+
+ void addf(const std::string& n, const std::string& d, Vars* v)
+ {
+ // This method is to capture in our own DescMap the name and
+ // description of every registered function, for metadata query
+ // testing.
+ descs[n] = d;
+ // Also capture the Vars instance on which each function should operate.
+ funcvars[n] = v;
+ // See constructor for rationale for setting these instance vars.
+ this->name = n;
+ this->desc = d;
+ }
+
+ void verify_descs()
+ {
+ // Copy descs to a temp map of same type.
+ DescMap forgotten(descs.begin(), descs.end());
+ // LLEventDispatcher intentionally provides only const_iterator:
+ // since dereferencing that iterator generates values on the fly,
+ // it's meaningless to have a modifiable iterator. But since our
+ // 'work' object isn't const, by default BOOST_FOREACH() wants to
+ // use non-const iterators. Persuade it to use the const_iterator.
+ foreach(LLEventDispatcher::NameDesc nd, const_cast<const Dispatcher&>(work))
+ {
+ DescMap::iterator found = forgotten.find(nd.first);
+ ensure(STRINGIZE("LLEventDispatcher records function '" << nd.first
+ << "' we didn't enter"),
+ found != forgotten.end());
+ ensure_equals(STRINGIZE("LLEventDispatcher desc '" << nd.second <<
+ "' doesn't match what we entered: '" << found->second << "'"),
+ nd.second, found->second);
+ // found in our map the name from LLEventDispatcher, good, erase
+ // our map entry
+ forgotten.erase(found);
+ }
+ if (! forgotten.empty())
+ {
+ std::ostringstream out;
+ out << "LLEventDispatcher failed to report";
+ const char* delim = ": ";
+ foreach(const DescMap::value_type& fme, forgotten)
+ {
+ out << delim << fme.first;
+ delim = ", ";
+ }
+ ensure(out.str(), false);
+ }
+ }
+
+ Vars* varsfor(const std::string& name)
+ {
+ VarsMap::const_iterator found = funcvars.find(name);
+ ensure(STRINGIZE("No Vars* for " << name), found != funcvars.end());
+ ensure(STRINGIZE("NULL Vars* for " << name), found->second);
+ return found->second;
+ }
+
+ void ensure_has(const std::string& outer, const std::string& inner)
+ {
+ ensure(STRINGIZE("'" << outer << "' does not contain '" << inner << "'").c_str(),
+ outer.find(inner) != std::string::npos);
+ }
+
+ void call_exc(const std::string& func, const LLSD& args, const std::string& exc_frag)
+ {
+ std::string threw;
+ try
+ {
+ work(func, args);
+ }
+ catch (const std::runtime_error& e)
+ {
+ cout << "*** " << e.what() << '\n';
+ threw = e.what();
+ }
+ ensure_has(threw, exc_frag);
+ }
+
+ LLSD getMetadata(const std::string& name)
+ {
+ LLSD meta(work.getMetadata(name));
+ ensure(STRINGIZE("No metadata for " << name), meta.isDefined());
+ return meta;
+ }
+
+ // From two related LLSD arrays, e.g. a param-names array and a values
+ // array, zip them together into an LLSD map.
+ LLSD zipmap(const LLSD& keys, const LLSD& values)
+ {
+ LLSD map;
+ for (LLSD::Integer i = 0, iend = keys.size(); i < iend; ++i)
+ {
+ // Have to select asString() since you can index an LLSD
+ // object with either String or Integer.
+ map[keys[i].asString()] = values[i];
+ }
+ return map;
+ }
+
+ // If I call this ensure_equals(), it blocks visibility of all other
+ // ensure_equals() overloads. Normally I could say 'using
+ // baseclass::ensure_equals;' and fix that, but I don't know what the
+ // base class is!
+ void ensure_llsd(const std::string& msg, const LLSD& actual, const LLSD& expected, U32 bits)
+ {
+ std::ostringstream out;
+ if (! msg.empty())
+ {
+ out << msg << ": ";
+ }
+ out << "expected " << expected << ", actual " << actual;
+ ensure(out.str(), llsd_equals(actual, expected, bits));
+ }
+
+ void ensure_llsd(const LLSD& actual, const LLSD& expected, U32 bits)
+ {
+ ensure_llsd("", actual, expected, bits);
+ }
+ };
+ typedef test_group<lleventdispatcher_data> lleventdispatcher_group;
+ typedef lleventdispatcher_group::object object;
+ lleventdispatcher_group lleventdispatchergrp("lleventdispatcher");
+
+ // Call cases:
+ // - (try_call | call) (explicit name | event key) (real | bogus) name
+ // - Callable with args that (do | do not) match required
+ // - (Free function | non-static method), no args, (array | map) style
+ // - (Free function | non-static method), arbitrary args,
+ // (array style with (scalar | map) | map style with scalar)
+ // - (Free function | non-static method), arbitrary args, array style with
+ // array (too short | too long | just right)
+ // [trap LL_WARNS for too-long case?]
+ // - (Free function | non-static method), arbitrary args, map style with
+ // (array | map) (all | too many | holes (with | without) defaults)
+ // - const char* param gets ("" | NULL)
+
+ // Query cases:
+ // - Iterate over all (with | without) remove()
+ // - getDispatchKey()
+ // - Callable style (with | without) required
+ // - (Free function | non-static method), array style, (no | arbitrary) params
+ // - (Free function | non-static method), map style, (no | arbitrary) params,
+ // (empty | full | partial (array | map)) defaults
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("map-style registration with non-array params");
+ // Pass "param names" as scalar or as map
+ LLSD attempts(LLSDArray(17)(LLSDMap("pi", 3.14)("two", 2)));
+ foreach(LLSD ae, inArray(attempts))
+ {
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena, ae);
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "must be an array");
+ }
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("map-style registration with badly-formed defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena, LLSDArray("a")("b"), 17);
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "must be a map or an array");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("map-style registration with too many array defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDArray(17)(0.9)("gack"));
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "shorter than");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("map-style registration with too many map defaults");
+ std::string threw;
+ try
+ {
+ work.add("freena_err", "freena", freena,
+ LLSDArray("a")("b"),
+ LLSDMap("b", 17)("foo", 3.14)("bar", "sinister"));
+ }
+ catch (const std::exception& e)
+ {
+ threw = e.what();
+ }
+ ensure_has(threw, "nonexistent params");
+ ensure_has(threw, "foo");
+ ensure_has(threw, "bar");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("query all");
+ verify_descs();
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("query all with remove()");
+ ensure("remove('bogus') returned true", ! work.remove("bogus"));
+ ensure("remove('real') returned false", work.remove("free1"));
+ // Of course, remove that from 'descs' too...
+ descs.erase("free1");
+ verify_descs();
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("getDispatchKey()");
+ ensure_equals(work.getDispatchKey(), "op");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("query Callables with/out required params");
+ LLSD names(LLSDArray("free1")("Dmethod1")("Dcmethod1")("method1"));
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure("should not have required structure", metadata["required"].isUndefined());
+ ensure("should not have optional", metadata["optional"].isUndefined());
+
+ std::string name_req(nm.asString() + "_req");
+ metadata = getMetadata(name_req);
+ ensure_equals(metadata["name"].asString(), name_req);
+ ensure_equals(metadata["desc"].asString(), descs[name_req]);
+ ensure_equals("required mismatch", required, metadata["required"]);
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("query array-style functions/methods");
+ // Associate each registered name with expected arity.
+ LLSD expected(LLSDArray
+ (LLSDArray
+ (0)(LLSDArray("free0_array")("smethod0_array")("method0_array")))
+ (LLSDArray
+ (5)(LLSDArray("freena_array")("smethodna_array")("methodna_array")))
+ (LLSDArray
+ (5)(LLSDArray("freenb_array")("smethodnb_array")("methodnb_array"))));
+ foreach(LLSD ae, inArray(expected))
+ {
+ LLSD::Integer arity(ae[0].asInteger());
+ LLSD names(ae[1]);
+ LLSD req(LLSD::emptyArray());
+ if (arity)
+ req[arity - 1] = LLSD();
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure_equals(STRINGIZE("mismatched required for " << nm.asString()),
+ metadata["required"], req);
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("query map-style no-params functions/methods");
+ // - (Free function | non-static method), map style, no params (ergo
+ // no defaults)
+ LLSD names(LLSDArray("free0_map")("smethod0_map")("method0_map"));
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(metadata["desc"].asString(), descs[nm]);
+ ensure("should not have required",
+ (metadata["required"].isUndefined() || metadata["required"].size() == 0));
+ ensure("should not have optional", metadata["optional"].isUndefined());
+ }
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ set_test_name("query map-style arbitrary-params functions/methods: "
+ "full array defaults vs. full map defaults");
+ // With functions registered with no defaults ("_allreq" suffixes),
+ // there is of course no difference between array defaults and map
+ // defaults. (We don't even bother registering with LLSD::emptyArray()
+ // vs. LLSD::emptyMap().) With functions registered with all defaults,
+ // there should (!) be no difference beween array defaults and map
+ // defaults. Verify, so we can ignore the distinction for all other
+ // tests.
+ LLSD equivalences(LLSDArray
+ (LLSDArray("freena_map_adft")("freena_map_mdft"))
+ (LLSDArray("freenb_map_adft")("freenb_map_mdft"))
+ (LLSDArray("smethodna_map_adft")("smethodna_map_mdft"))
+ (LLSDArray("smethodnb_map_adft")("smethodnb_map_mdft"))
+ (LLSDArray("methodna_map_adft")("methodna_map_mdft"))
+ (LLSDArray("methodnb_map_adft")("methodnb_map_mdft")));
+ foreach(LLSD eq, inArray(equivalences))
+ {
+ LLSD adft(eq[0]);
+ LLSD mdft(eq[1]);
+ // We can't just compare the results of the two getMetadata()
+ // calls, because they contain ["name"], which are different. So
+ // capture them, verify that each ["name"] is as expected, then
+ // remove for comparing the rest.
+ LLSD ameta(getMetadata(adft));
+ LLSD mmeta(getMetadata(mdft));
+ ensure_equals("adft name", adft, ameta["name"]);
+ ensure_equals("mdft name", mdft, mmeta["name"]);
+ ameta.erase("name");
+ mmeta.erase("name");
+ ensure_equals(STRINGIZE("metadata for " << adft.asString()
+ << " vs. " << mdft.asString()),
+ ameta, mmeta);
+ }
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ set_test_name("query map-style arbitrary-params functions/methods");
+ // - (Free function | non-static method), map style, arbitrary params,
+ // (empty | full | partial (array | map)) defaults
+
+ // Generate maps containing all parameter names for cases in which all
+ // params are required. Also maps containing left requirements for
+ // partial defaults arrays. Also defaults maps from defaults arrays.
+ LLSD allreq, leftreq, rightdft;
+ foreach(LLSD::String a, ab)
+ {
+ // The map in which all params are required uses params[a] as
+ // keys, with all isUndefined() as values. We can accomplish that
+ // by passing zipmap() an empty values array.
+ allreq[a] = zipmap(params[a], LLSD::emptyArray());
+ // Same for leftreq, save that we use the subset of the params not
+ // supplied by dft_array_partial[a].
+ LLSD::Integer partition(params[a].size() - dft_array_partial[a].size());
+ leftreq[a] = zipmap(llsd_copy_array(params[a].beginArray(),
+ params[a].beginArray() + partition),
+ LLSD::emptyArray());
+ // Generate map pairing dft_array_partial[a] values with their
+ // param names.
+ rightdft[a] = zipmap(llsd_copy_array(params[a].beginArray() + partition,
+ params[a].endArray()),
+ dft_array_partial[a]);
+ }
+ cout << "allreq:\n" << allreq << "\nleftreq:\n" << leftreq << "\nrightdft:\n" << rightdft << std::endl;
+
+ // Generate maps containing parameter names not provided by the
+ // dft_map_partial maps.
+ LLSD skipreq(allreq);
+ foreach(LLSD::String a, ab)
+ {
+ foreach(const MapEntry& me, inMap(dft_map_partial[a]))
+ {
+ skipreq[a].erase(me.first);
+ }
+ }
+ cout << "skipreq:\n" << skipreq << std::endl;
+
+ LLSD groups(LLSDArray // array of groups
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_allreq")("smethodna_map_allreq")("methodna_map_allreq"))
+ (LLSDArray(allreq["a"])(LLSD()))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_allreq")("smethodnb_map_allreq")("methodnb_map_allreq"))
+ (LLSDArray(allreq["b"])(LLSD()))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq"))
+ (LLSDArray(leftreq["a"])(rightdft["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq"))
+ (LLSDArray(leftreq["b"])(rightdft["b"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq"))
+ (LLSDArray(skipreq["a"])(dft_map_partial["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq"))
+ (LLSDArray(skipreq["b"])(dft_map_partial["b"]))) // required, optional
+
+ // We only need mention the full-map-defaults ("_mdft" suffix)
+ // registrations, having established their equivalence with the
+ // full-array-defaults ("_adft" suffix) registrations in another test.
+ (LLSDArray // group
+ (LLSDArray("freena_map_mdft")("smethodna_map_mdft")("methodna_map_mdft"))
+ (LLSDArray(LLSD::emptyMap())(dft_map_full["a"]))) // required, optional
+
+ (LLSDArray // group
+ (LLSDArray("freenb_map_mdft")("smethodnb_map_mdft")("methodnb_map_mdft"))
+ (LLSDArray(LLSD::emptyMap())(dft_map_full["b"])))); // required, optional
+
+ foreach(LLSD grp, inArray(groups))
+ {
+ // Internal structure of each group in 'groups':
+ LLSD names(grp[0]);
+ LLSD required(grp[1][0]);
+ LLSD optional(grp[1][1]);
+ cout << "For " << names << ",\n" << "required:\n" << required << "\noptional:\n" << optional << std::endl;
+
+ // Loop through 'names'
+ foreach(LLSD nm, inArray(names))
+ {
+ LLSD metadata(getMetadata(nm));
+ ensure_equals("name mismatch", metadata["name"], nm);
+ ensure_equals(nm.asString(), metadata["desc"].asString(), descs[nm]);
+ ensure_equals(STRINGIZE(nm << " required mismatch"),
+ metadata["required"], required);
+ ensure_equals(STRINGIZE(nm << " optional mismatch"),
+ metadata["optional"], optional);
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ set_test_name("try_call()");
+ ensure("try_call(bogus name, LLSD()) returned true", ! work.try_call("freek", LLSD()));
+ ensure("try_call(bogus name) returned true", ! work.try_call(LLSDMap("op", "freek")));
+ ensure("try_call(real name, LLSD()) returned false", work.try_call("free0_array", LLSD()));
+ ensure("try_call(real name) returned false", work.try_call(LLSDMap("op", "free0_map")));
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ set_test_name("call with bad name");
+ call_exc("freek", LLSD(), "not found");
+ // We don't have a comparable helper function for the one-arg
+ // operator() method, and it's not worth building one just for this
+ // case. Write it out.
+ std::string threw;
+ try
+ {
+ work(LLSDMap("op", "freek"));
+ }
+ catch (const std::runtime_error& e)
+ {
+ cout << "*** " << e.what() << "\n";
+ threw = e.what();
+ }
+ ensure_has(threw, "bad");
+ ensure_has(threw, "op");
+ ensure_has(threw, "freek");
+ }
+
+ template<> template<>
+ void object::test<15>()
+ {
+ set_test_name("call with event key");
+ // We don't need a separate test for operator()(string, LLSD) with
+ // valid name, because all the rest of the tests exercise that case.
+ // The one we don't exercise elsewhere is operator()(LLSD) with valid
+ // name, so here it is.
+ work(LLSDMap("op", "free0_map"));
+ ensure_equals(g.i, 17);
+ }
+
+ // Cannot be defined inside function body... remind me again why we use C++... :-P
+ struct CallablesTriple
+ {
+ std::string name, name_req;
+ LLSD& llsd;
+ };
+
+ template<> template<>
+ void object::test<16>()
+ {
+ set_test_name("call Callables");
+ CallablesTriple tests[] =
+ {
+ { "free1", "free1_req", g.llsd },
+ { "Dmethod1", "Dmethod1_req", work.llsd },
+ { "Dcmethod1", "Dcmethod1_req", work.llsd },
+ { "method1", "method1_req", v.llsd }
+ };
+ // Arbitrary LLSD value that we should be able to pass to Callables
+ // without 'required', but should not be able to pass to Callables
+ // with 'required'.
+ LLSD answer(42);
+ // LLSD value matching 'required' according to llsd_matches() rules.
+ LLSD matching(LLSDMap("d", 3.14)("array", LLSDArray("answer")(true)(answer)));
+ // Okay, walk through 'tests'.
+ foreach(const CallablesTriple& tr, tests)
+ {
+ // Should be able to pass 'answer' to Callables registered
+ // without 'required'.
+ work(tr.name, answer);
+ ensure_equals("answer mismatch", tr.llsd, answer);
+ // Should NOT be able to pass 'answer' to Callables registered
+ // with 'required'.
+ call_exc(tr.name_req, answer, "bad request");
+ // But SHOULD be able to pass 'matching' to Callables registered
+ // with 'required'.
+ work(tr.name_req, matching);
+ ensure_equals("matching mismatch", tr.llsd, matching);
+ }
+ }
+
+ template<> template<>
+ void object::test<17>()
+ {
+ set_test_name("passing wrong args to (map | array)-style registrations");
+
+ // Pass scalar/map to array-style functions, scalar/array to map-style
+ // functions. As that validation happens well before we engage the
+ // argument magic, it seems pointless to repeat this with every
+ // variation: (free function | non-static method), (no | arbitrary)
+ // args. We should only need to engage it for one map-style
+ // registration and one array-style registration.
+ std::string array_exc("needs an args array");
+ call_exc("free0_array", 17, array_exc);
+ call_exc("free0_array", LLSDMap("pi", 3.14), array_exc);
+
+ std::string map_exc("needs a map");
+ call_exc("free0_map", 17, map_exc);
+ // Passing an array to a map-style function works now! No longer an
+ // error case!
+// call_exc("free0_map", LLSDArray("a")("b"), map_exc);
+ }
+
+ template<> template<>
+ void object::test<18>()
+ {
+ set_test_name("call no-args functions");
+ LLSD names(LLSDArray
+ ("free0_array")("free0_map")
+ ("smethod0_array")("smethod0_map")
+ ("method0_array")("method0_map"));
+ foreach(LLSD name, inArray(names))
+ {
+ // Look up the Vars instance for this function.
+ Vars* vars(varsfor(name));
+ // Both the global and stack Vars instances are automatically
+ // cleared at the start of each test<n> method. But since we're
+ // calling these things several different times in the same
+ // test<n> method, manually reset the Vars between each.
+ *vars = Vars();
+ ensure_equals(vars->i, 0);
+ // call function with empty array (or LLSD(), should be equivalent)
+ work(name, LLSD());
+ ensure_equals(vars->i, 17);
+ }
+ }
+
+ // Break out this data because we use it in a couple different tests.
+ LLSD array_funcs(LLSDArray
+ (LLSDMap("a", "freena_array") ("b", "freenb_array"))
+ (LLSDMap("a", "smethodna_array")("b", "smethodnb_array"))
+ (LLSDMap("a", "methodna_array") ("b", "methodnb_array")));
+
+ template<> template<>
+ void object::test<19>()
+ {
+ set_test_name("call array-style functions with too-short arrays");
+ // Could have two different too-short arrays, one for *na and one for
+ // *nb, but since they both take 5 params...
+ LLSD tooshort(LLSDArray("this")("array")("too")("short"));
+ foreach(const LLSD& funcsab, inArray(array_funcs))
+ {
+ foreach(const llsd::MapEntry& e, inMap(funcsab))
+ {
+ call_exc(e.second, tooshort, "requires more arguments");
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<20>()
+ {
+ set_test_name("call array-style functions with (just right | too long) arrays");
+ std::vector<U8> binary;
+ for (size_t h(0x01), i(0); i < 5; h+= 0x22, ++i)
+ {
+ binary.push_back(h);
+ }
+ LLSD args(LLSDMap("a", LLSDArray(true)(17)(3.14)(123.456)("char*"))
+ ("b", LLSDArray("string")
+ (LLUUID("01234567-89ab-cdef-0123-456789abcdef"))
+ (LLDate("2011-02-03T15:07:00Z"))
+ (LLURI("http://secondlife.com"))
+ (binary)));
+ LLSD argsplus(args);
+ argsplus["a"].append("bogus");
+ argsplus["b"].append("bogus");
+ LLSD expect;
+ foreach(LLSD::String a, ab)
+ {
+ expect[a] = zipmap(params[a], args[a]);
+ }
+ // Adjust expect["a"]["cp"] for special Vars::cp treatment.
+ expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ cout << "expect: " << expect << '\n';
+
+ // Use substantially the same logic for args and argsplus
+ LLSD argsarrays(LLSDArray(args)(argsplus));
+ // So i==0 selects 'args', i==1 selects argsplus
+ for (LLSD::Integer i(0), iend(argsarrays.size()); i < iend; ++i)
+ {
+ foreach(const LLSD& funcsab, inArray(array_funcs))
+ {
+ foreach(LLSD::String a, ab)
+ {
+ // Reset the Vars instance before each call
+ Vars* vars(varsfor(funcsab[a]));
+ *vars = Vars();
+ work(funcsab[a], argsarrays[i][a]);
+ ensure_llsd(STRINGIZE(funcsab[a].asString() <<
+ ": expect[\"" << a << "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits ~= 2 decimal digits
+
+ // TODO: in the i==1 or argsplus case, intercept LL_WARNS
+ // output? Even without that, using argsplus verifies that
+ // passing too many args isn't fatal; it works -- but
+ // would be nice to notice the warning too.
+ }
+ }
+ }
+ }
+
+ template<> template<>
+ void object::test<21>()
+ {
+ set_test_name("verify that passing LLSD() to const char* sends NULL");
+
+ ensure_equals("Vars::cp init", v.cp, "");
+ work("methodna_map_mdft", LLSDMap("cp", LLSD()));
+ ensure_equals("passing LLSD()", v.cp, "NULL");
+ work("methodna_map_mdft", LLSDMap("cp", ""));
+ ensure_equals("passing \"\"", v.cp, "''");
+ work("methodna_map_mdft", LLSDMap("cp", "non-NULL"));
+ ensure_equals("passing \"non-NULL\"", v.cp, "'non-NULL'");
+ }
+
+ template<> template<>
+ void object::test<22>()
+ {
+ set_test_name("call map-style functions with (full | oversized) (arrays | maps)");
+ const char binary[] = "\x99\x88\x77\x66\x55";
+ LLSD array_full(LLSDMap
+ ("a", LLSDArray(false)(255)(98.6)(1024.5)("pointer"))
+ ("b", LLSDArray("object")(LLUUID::generateNewID())(LLDate::now())(LLURI("http://wiki.lindenlab.com/wiki"))(LLSD::Binary(boost::begin(binary), boost::end(binary)))));
+ LLSD array_overfull(array_full);
+ foreach(LLSD::String a, ab)
+ {
+ array_overfull[a].append("bogus");
+ }
+ cout << "array_full: " << array_full << "\narray_overfull: " << array_overfull << std::endl;
+ // We rather hope that LLDate::now() will generate a timestamp
+ // distinct from the one it generated in the constructor, moments ago.
+ ensure_not_equals("Timestamps too close",
+ array_full["b"][2].asDate(), dft_array_full["b"][2].asDate());
+ // We /insist/ that LLUUID::generateNewID() do so.
+ ensure_not_equals("UUID collision",
+ array_full["b"][1].asUUID(), dft_array_full["b"][1].asUUID());
+ LLSD map_full, map_overfull;
+ foreach(LLSD::String a, ab)
+ {
+ map_full[a] = zipmap(params[a], array_full[a]);
+ map_overfull[a] = map_full[a];
+ map_overfull[a]["extra"] = "ignore";
+ }
+ cout << "map_full: " << map_full << "\nmap_overfull: " << map_overfull << std::endl;
+ LLSD expect(map_full);
+ // Twiddle the const char* param.
+ expect["a"]["cp"] = std::string("'") + expect["a"]["cp"].asString() + "'";
+ // Another adjustment. For each data type, we're trying to distinguish
+ // three values: the Vars member's initial value (member wasn't
+ // stored; control never reached the set function), the registered
+ // default param value from dft_array_full, and the array_full value
+ // in this test. But bool can only distinguish two values. In this
+ // case, we want to differentiate the local array_full value from the
+ // dft_array_full value, so we use 'false'. However, that means
+ // Vars::inspect() doesn't differentiate it from the initial value,
+ // so won't bother returning it. Predict that behavior to match the
+ // LLSD values.
+ expect["a"].erase("b");
+ cout << "expect: " << expect << std::endl;
+ // For this test, calling functions registered with different sets of
+ // parameter defaults should make NO DIFFERENCE WHATSOEVER. Every call
+ // should pass all params.
+ LLSD names(LLSDMap
+ ("a", LLSDArray
+ ("freena_map_allreq") ("smethodna_map_allreq") ("methodna_map_allreq")
+ ("freena_map_leftreq")("smethodna_map_leftreq")("methodna_map_leftreq")
+ ("freena_map_skipreq")("smethodna_map_skipreq")("methodna_map_skipreq")
+ ("freena_map_adft") ("smethodna_map_adft") ("methodna_map_adft")
+ ("freena_map_mdft") ("smethodna_map_mdft") ("methodna_map_mdft"))
+ ("b", LLSDArray
+ ("freenb_map_allreq") ("smethodnb_map_allreq") ("methodnb_map_allreq")
+ ("freenb_map_leftreq")("smethodnb_map_leftreq")("methodnb_map_leftreq")
+ ("freenb_map_skipreq")("smethodnb_map_skipreq")("methodnb_map_skipreq")
+ ("freenb_map_adft") ("smethodnb_map_adft") ("methodnb_map_adft")
+ ("freenb_map_mdft") ("smethodnb_map_mdft") ("methodnb_map_mdft")));
+ // Treat (full | overfull) (array | map) the same.
+ LLSD argssets(LLSDArray(array_full)(array_overfull)(map_full)(map_overfull));
+ foreach(const LLSD& args, inArray(argssets))
+ {
+ foreach(LLSD::String a, ab)
+ {
+ foreach(LLSD::String name, inArray(names[a]))
+ {
+ // Reset the Vars instance
+ Vars* vars(varsfor(name));
+ *vars = Vars();
+ work(name, args[a]);
+ ensure_llsd(STRINGIZE(name << ": expect[\"" << a << "\"] mismatch"),
+ vars->inspect(), expect[a], 7); // 7 bits, 2 decimal digits
+ // intercept LL_WARNS for the two overfull cases?
+ }
+ }
+ }
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp
index 28b909298e..2cdfb52f2f 100644..100755
--- a/indra/llcommon/tests/lleventfilter_test.cpp
+++ b/indra/llcommon/tests/lleventfilter_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-03-06
* @brief Test for lleventfilter.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -227,7 +244,7 @@ namespace tut
void filter_object::test<4>()
{
set_test_name("LLEventTimeout::errorAfter()");
- WrapLL_ERRS capture;
+ WrapLLErrs capture;
LLEventPump& driver(pumps.obtain("driver"));
TestEventTimeout filter(driver);
listener0.reset(0);
@@ -257,7 +274,7 @@ namespace tut
{
mainloop.post(17);
}
- catch (const WrapLL_ERRS::FatalException& e)
+ catch (const WrapLLErrs::FatalException& e)
{
threw = e.what();
}
diff --git a/indra/llcommon/tests/llframetimer_test.cpp b/indra/llcommon/tests/llframetimer_test.cpp
index 1d047e41f8..be372bb855 100644..100755
--- a/indra/llcommon/tests/llframetimer_test.cpp
+++ b/indra/llcommon/tests/llframetimer_test.cpp
@@ -3,31 +3,25 @@
* @date 2006-07-23
* @brief Tests the timers.
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -49,7 +43,7 @@ namespace tut
};
typedef test_group<frametimer_test> frametimer_group_t;
typedef frametimer_group_t::object frametimer_object_t;
- tut::frametimer_group_t frametimer_instance("frametimer");
+ tut::frametimer_group_t frametimer_instance("LLFrameTimer");
template<> template<>
void frametimer_object_t::test<1>()
@@ -90,25 +84,34 @@ namespace tut
template<> template<>
void frametimer_object_t::test<3>()
{
+ clock_t t1 = clock();
+ ms_sleep(200);
+ clock_t t2 = clock();
+ clock_t elapsed = t2 - t1 + 1;
+ std::cout << "Note: using clock(), ms_sleep() actually took " << (long)elapsed << "ms" << std::endl;
+
F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds();
seconds_since_epoch += 2.0;
LLFrameTimer timer;
timer.setExpiryAt(seconds_since_epoch);
- ensure("timer not expired on create", !timer.hasExpired());
- int ii;
- for(ii = 0; ii < 10; ++ii)
+ /*
+ * Note that the ms_sleep(200) below is only guaranteed to return
+ * in 200ms _or_more_, so it should be true that by the 10th
+ * iteration we've gotten to the 2 seconds requested above
+ * and the timer should expire, but it can expire in fewer iterations
+ * if one or more of the ms_sleep calls takes longer.
+ * (as it did when we moved to Mac OS X 10.10)
+ */
+ int iterations_until_expiration = 0;
+ while ( !timer.hasExpired() )
{
- ms_sleep(150);
- LLFrameTimer::updateFrameTime();
- }
- ensure("timer not expired after a bit", !timer.hasExpired());
- for(ii = 0; ii < 10; ++ii)
- {
- ms_sleep(100);
- LLFrameTimer::updateFrameTime();
+ ms_sleep(200);
+ LLFrameTimer::updateFrameTime();
+ iterations_until_expiration++;
}
- ensure("timer expired", timer.hasExpired());
+ ensure("timer took too long to expire", iterations_until_expiration <= 10);
}
+
/*
template<> template<>
void frametimer_object_t::test<4>()
diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp
index 4bb3ec2922..c7d4b8a06b 100644..100755
--- a/indra/llcommon/tests/llinstancetracker_test.cpp
+++ b/indra/llcommon/tests/llinstancetracker_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-11-10
* @brief Test for llinstancetracker.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -18,11 +35,18 @@
#include <vector>
#include <set>
#include <algorithm> // std::sort()
+#include <stdexcept>
// std headers
// external library headers
#include <boost/scoped_ptr.hpp>
// other Linden headers
#include "../test/lltut.h"
+#include "wrapllerrs.h"
+
+struct Badness: public std::runtime_error
+{
+ Badness(const std::string& what): std::runtime_error(what) {}
+};
struct Keyed: public LLInstanceTracker<Keyed, std::string>
{
@@ -35,6 +59,17 @@ struct Keyed: public LLInstanceTracker<Keyed, std::string>
struct Unkeyed: public LLInstanceTracker<Unkeyed>
{
+ Unkeyed(const std::string& thrw="")
+ {
+ // LLInstanceTracker should respond appropriately if a subclass
+ // constructor throws an exception. Specifically, it should run
+ // LLInstanceTracker's destructor and remove itself from the
+ // underlying container.
+ if (! thrw.empty())
+ {
+ throw Badness(thrw);
+ }
+ }
};
/*****************************************************************************
@@ -77,6 +112,7 @@ namespace tut
void object::test<2>()
{
ensure_equals(Unkeyed::instanceCount(), 0);
+ Unkeyed* dangling = NULL;
{
Unkeyed one;
ensure_equals(Unkeyed::instanceCount(), 1);
@@ -89,7 +125,11 @@ namespace tut
ensure_equals(found, two.get());
}
ensure_equals(Unkeyed::instanceCount(), 1);
- }
+ // store an unwise pointer to a temp Unkeyed instance
+ dangling = &one;
+ } // make that instance vanish
+ // check the now-invalid pointer to the destroyed instance
+ ensure("getInstance(T*) failed to track destruction", ! Unkeyed::getInstance(dangling));
ensure_equals(Unkeyed::instanceCount(), 0);
}
@@ -134,33 +174,125 @@ namespace tut
{
Unkeyed one, two, three;
typedef std::set<Unkeyed*> KeySet;
- KeySet keys;
- keys.insert(&one);
- keys.insert(&two);
- keys.insert(&three);
- {
- Unkeyed::LLInstanceTrackerScopedGuard guard;
- for (Unkeyed::key_iter ki(guard.beginKeys()), kend(guard.endKeys());
- ki != kend; ++ki)
- {
- ensure_equals("spurious key", keys.erase(*ki), 1);
- }
- }
- ensure_equals("unreported key", keys.size(), 0);
-
+
KeySet instances;
instances.insert(&one);
instances.insert(&two);
instances.insert(&three);
- {
- Unkeyed::LLInstanceTrackerScopedGuard guard;
- for (Unkeyed::instance_iter ii(guard.beginInstances()), iend(guard.endInstances());
- ii != iend; ++ii)
+
+ for (Unkeyed::instance_iter ii(Unkeyed::beginInstances()), iend(Unkeyed::endInstances()); ii != iend; ++ii)
{
Unkeyed& ref = *ii;
ensure_equals("spurious instance", instances.erase(&ref), 1);
}
- }
+
ensure_equals("unreported instance", instances.size(), 0);
}
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("delete Keyed with outstanding instance_iter");
+ std::string what;
+ Keyed* keyed = new Keyed("delete Keyed with outstanding instance_iter");
+ {
+ WrapLLErrs wrapper;
+ Keyed::instance_iter i(Keyed::beginInstances());
+ try
+ {
+ delete keyed;
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("delete Keyed with outstanding key_iter");
+ std::string what;
+ Keyed* keyed = new Keyed("delete Keyed with outstanding key_it");
+ {
+ WrapLLErrs wrapper;
+ Keyed::key_iter i(Keyed::beginKeys());
+ try
+ {
+ delete keyed;
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("delete Unkeyed with outstanding instance_iter");
+ std::string what;
+ Unkeyed* unkeyed = new Unkeyed;
+ {
+ WrapLLErrs wrapper;
+ Unkeyed::instance_iter i(Unkeyed::beginInstances());
+ try
+ {
+ delete unkeyed;
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ what = e.what();
+ }
+ }
+ ensure(! what.empty());
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("exception in subclass ctor");
+ typedef std::set<Unkeyed*> InstanceSet;
+ InstanceSet existing;
+ // We can't use the iterator-range InstanceSet constructor because
+ // beginInstances() returns an iterator that dereferences to an
+ // Unkeyed&, not an Unkeyed*.
+ for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+ ukend(Unkeyed::endInstances());
+ uki != ukend; ++uki)
+ {
+ existing.insert(&*uki);
+ }
+ try
+ {
+ // We don't expect the assignment to take place because we expect
+ // Unkeyed to respond to the non-empty string param by throwing.
+ // We know the LLInstanceTracker base-class constructor will have
+ // run before Unkeyed's constructor, therefore the new instance
+ // will have added itself to the underlying set. The whole
+ // question is, when Unkeyed's constructor throws, will
+ // LLInstanceTracker's destructor remove it from the set? I
+ // realize we're testing the C++ implementation more than
+ // Unkeyed's implementation, but this seems an important point to
+ // nail down.
+ new Unkeyed("throw");
+ }
+ catch (const Badness&)
+ {
+ }
+ // Ensure that every member of the new, updated set of Unkeyed
+ // instances was also present in the original set. If that's not true,
+ // it's because our new Unkeyed ended up in the updated set despite
+ // its constructor exception.
+ for (Unkeyed::instance_iter uki(Unkeyed::beginInstances()),
+ ukend(Unkeyed::endInstances());
+ uki != ukend; ++uki)
+ {
+ ensure("failed to remove instance", existing.find(&*uki) != existing.end());
+ }
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/lllazy_test.cpp b/indra/llcommon/tests/lllazy_test.cpp
index db581d650f..32a717f4fc 100644..100755
--- a/indra/llcommon/tests/lllazy_test.cpp
+++ b/indra/llcommon/tests/lllazy_test.cpp
@@ -4,8 +4,25 @@
* @date 2009-01-28
* @brief Tests of lllazy.h.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
new file mode 100755
index 0000000000..2d88e2c676
--- /dev/null
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -0,0 +1,671 @@
+/**
+ * @file llleap_test.cpp
+ * @author Nat Goodspeed
+ * @date 2012-02-21
+ * @brief Test for llleap.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llleap.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/assign/list_of.hpp>
+#include <boost/phoenix/core/argument.hpp>
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "../test/catch_and_store_what_in.h"
+#include "wrapllerrs.h"
+#include "llevents.h"
+#include "llprocess.h"
+#include "stringize.h"
+#include "StringVec.h"
+#include <functional>
+
+using boost::assign::list_of;
+
+StringVec sv(const StringVec& listof) { return listof; }
+
+#if defined(LL_WINDOWS)
+#define sleep(secs) _sleep((secs) * 1000)
+#endif
+
+const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data
+
+void waitfor(const std::vector<LLLeap*>& instances, int timeout=60)
+{
+ int i;
+ for (i = 0; i < timeout; ++i)
+ {
+ // Every iteration, test whether any of the passed LLLeap instances
+ // still exist (are still running).
+ std::vector<LLLeap*>::const_iterator vli(instances.begin()), vlend(instances.end());
+ for ( ; vli != vlend; ++vli)
+ {
+ // getInstance() returns NULL if it's terminated/gone, non-NULL if
+ // it's still running
+ if (LLLeap::getInstance(*vli))
+ break;
+ }
+ // If we made it through all of 'instances' without finding one that's
+ // still running, we're done.
+ if (vli == vlend)
+ {
+/*==========================================================================*|
+ std::cout << instances.size() << " LLLeap instances terminated in "
+ << i << " seconds, proceeding" << std::endl;
+|*==========================================================================*/
+ return;
+ }
+ // Found an instance that's still running. Wait and pump LLProcess.
+ sleep(1);
+ LLEventPumps::instance().obtain("mainloop").post(LLSD());
+ }
+ tut::ensure(STRINGIZE("at least 1 of " << instances.size()
+ << " LLLeap instances timed out ("
+ << timeout << " seconds) without terminating"),
+ i < timeout);
+}
+
+void waitfor(LLLeap* instance, int timeout=60)
+{
+ std::vector<LLLeap*> instances;
+ instances.push_back(instance);
+ waitfor(instances, timeout);
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llleap_data
+ {
+ llleap_data():
+ reader(".py",
+ // This logic is adapted from vita.viewerclient.receiveEvent()
+ boost::phoenix::placeholders::arg1 <<
+ "import re\n"
+ "import os\n"
+ "import sys\n"
+ "\n"
+ // Don't forget that this Python script is written to some
+ // temp directory somewhere! Its __file__ is useless in
+ // finding indra/lib/python. Use our __FILE__, with
+ // raw-string syntax to deal with Windows pathnames.
+ "mydir = os.path.dirname(r'" << __FILE__ << "')\n"
+ // We expect mydir to be .../indra/llcommon/tests.
+ "sys.path.insert(0,\n"
+ " os.path.join(mydir, os.pardir, os.pardir, 'lib', 'python'))\n"
+ "from indra.base import llsd\n"
+ "\n"
+ "class ProtocolError(Exception):\n"
+ " def __init__(self, msg, data):\n"
+ " Exception.__init__(self, msg)\n"
+ " self.data = data\n"
+ "\n"
+ "class ParseError(ProtocolError):\n"
+ " pass\n"
+ "\n"
+ "def get():\n"
+ " hdr = ''\n"
+ " while ':' not in hdr and len(hdr) < 20:\n"
+ " hdr += sys.stdin.read(1)\n"
+ " if not hdr:\n"
+ " sys.exit(0)\n"
+ " if not hdr.endswith(':'):\n"
+ " raise ProtocolError('Expected len:data, got %r' % hdr, hdr)\n"
+ " try:\n"
+ " length = int(hdr[:-1])\n"
+ " except ValueError:\n"
+ " raise ProtocolError('Non-numeric len %r' % hdr[:-1], hdr[:-1])\n"
+ " parts = []\n"
+ " received = 0\n"
+ " while received < length:\n"
+ " parts.append(sys.stdin.read(length - received))\n"
+ " received += len(parts[-1])\n"
+ " data = ''.join(parts)\n"
+ " assert len(data) == length\n"
+ " try:\n"
+ " return llsd.parse(data)\n"
+ // Seems the old indra.base.llsd module didn't properly
+ // convert IndexError (from running off end of string) to
+ // LLSDParseError.
+ " except (IndexError, llsd.LLSDParseError), e:\n"
+ " msg = 'Bad received packet (%s)' % e\n"
+ " print >>sys.stderr, '%s, %s bytes:' % (msg, len(data))\n"
+ " showmax = 40\n"
+ // We've observed failures with very large packets;
+ // dumping the entire packet wastes time and space.
+ // But if the error states a particular byte offset,
+ // truncate to (near) that offset when dumping data.
+ " location = re.search(r' at (byte|index) ([0-9]+)', str(e))\n"
+ " if not location:\n"
+ " # didn't find offset, dump whole thing, no ellipsis\n"
+ " ellipsis = ''\n"
+ " else:\n"
+ " # found offset within error message\n"
+ " trunc = int(location.group(2)) + showmax\n"
+ " data = data[:trunc]\n"
+ " ellipsis = '... (%s more)' % (length - trunc)\n"
+ " offset = -showmax\n"
+ " for offset in xrange(0, len(data)-showmax, showmax):\n"
+ " print >>sys.stderr, '%04d: %r +' % \\\n"
+ " (offset, data[offset:offset+showmax])\n"
+ " offset += showmax\n"
+ " print >>sys.stderr, '%04d: %r%s' % \\\n"
+ " (offset, data[offset:], ellipsis)\n"
+ " raise ParseError(msg, data)\n"
+ "\n"
+ "# deal with initial stdin message\n"
+ // this will throw if the initial write to stdin doesn't
+ // follow len:data protocol, or if we couldn't find 'pump'
+ // in the dict
+ "_reply = get()['pump']\n"
+ "\n"
+ "def replypump():\n"
+ " return _reply\n"
+ "\n"
+ "def put(req):\n"
+ " sys.stdout.write(':'.join((str(len(req)), req)))\n"
+ " sys.stdout.flush()\n"
+ "\n"
+ "def send(pump, data):\n"
+ " put(llsd.format_notation(dict(pump=pump, data=data)))\n"
+ "\n"
+ "def request(pump, data):\n"
+ " # we expect 'data' is a dict\n"
+ " data['reply'] = _reply\n"
+ " send(pump, data)\n"),
+ // Get the actual pathname of the NamedExtTempFile and trim off
+ // the ".py" extension. (We could cache reader.getName() in a
+ // separate member variable, but I happen to know getName() just
+ // returns a NamedExtTempFile member rather than performing any
+ // computation, so I don't mind calling it twice.) Then take the
+ // basename.
+ reader_module(LLProcess::basename(
+ reader.getName().substr(0, reader.getName().length()-3))),
+ pPYTHON(getenv("PYTHON")),
+ PYTHON(pPYTHON? pPYTHON : "")
+ {
+ ensure("Set PYTHON to interpreter pathname", pPYTHON);
+ }
+ NamedExtTempFile reader;
+ const std::string reader_module;
+ const char* pPYTHON;
+ const std::string PYTHON;
+ };
+ typedef test_group<llleap_data> llleap_group;
+ typedef llleap_group::object object;
+ llleap_group llleapgrp("llleap");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("multiple LLLeap instances");
+ NamedTempFile script("py",
+ "import time\n"
+ "time.sleep(1)\n");
+ std::vector<LLLeap*> instances;
+ instances.push_back(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ instances.push_back(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ // In this case we're simply establishing that two LLLeap instances
+ // can coexist without throwing exceptions or bombing in any other
+ // way. Wait for them to terminate.
+ waitfor(instances);
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("stderr to log");
+ NamedTempFile script("py",
+ "import sys\n"
+ "sys.stderr.write('''Hello from Python!\n"
+ "note partial line''')\n");
+ CaptureLog log(LLError::LEVEL_INFO);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ log.messageWith("Hello from Python!");
+ log.messageWith("note partial line");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("bad stdout protocol");
+ NamedTempFile script("py",
+ "print 'Hello from Python!'\n");
+ CaptureLog log(LLError::LEVEL_WARN);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ ensure_contains("error log line",
+ log.messageWith("invalid protocol"), "Hello from Python!");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("leftover stdout");
+ NamedTempFile script("py",
+ "import sys\n"
+ // note lack of newline
+ "sys.stdout.write('Hello from Python!')\n");
+ CaptureLog log(LLError::LEVEL_WARN);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ ensure_contains("error log line",
+ log.messageWith("Discarding"), "Hello from Python!");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("bad stdout len prefix");
+ NamedTempFile script("py",
+ "import sys\n"
+ "sys.stdout.write('5a2:something')\n");
+ CaptureLog log(LLError::LEVEL_WARN);
+ waitfor(LLLeap::create(get_test_name(),
+ sv(list_of(PYTHON)(script.getName()))));
+ ensure_contains("error log line",
+ log.messageWith("invalid protocol"), "5a2:");
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("empty plugin vector");
+ std::string threw;
+ try
+ {
+ LLLeap::create("empty", StringVec());
+ }
+ CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+ ensure_contains("LLLeap::Error", threw, "no plugin");
+ // try the suppress-exception variant
+ ensure("bad launch returned non-NULL", ! LLLeap::create("empty", StringVec(), false));
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("bad launch");
+ // Synthesize bogus executable name
+ std::string BADPYTHON(PYTHON.substr(0, PYTHON.length()-1) + "x");
+ CaptureLog log;
+ std::string threw;
+ try
+ {
+ LLLeap::create("bad exe", BADPYTHON);
+ }
+ CATCH_AND_STORE_WHAT_IN(threw, LLLeap::Error)
+ ensure_contains("LLLeap::create() didn't throw", threw, "failed");
+ log.messageWith("failed");
+ log.messageWith(BADPYTHON);
+ // try the suppress-exception variant
+ ensure("bad launch returned non-NULL", ! LLLeap::create("bad exe", BADPYTHON, false));
+ }
+
+ // Generic self-contained listener: derive from this and override its
+ // call() method, then tell somebody to post on the pump named getName().
+ // Control will reach your call() override.
+ struct ListenerBase
+ {
+ // Pass the pump name you want; will tweak for uniqueness.
+ ListenerBase(const std::string& name):
+ mPump(name, true)
+ {
+ mPump.listen(name, boost::bind(&ListenerBase::call, this, _1));
+ }
+
+ virtual ~ListenerBase() {} // pacify MSVC
+
+ virtual bool call(const LLSD& request)
+ {
+ return false;
+ }
+
+ LLEventPump& getPump() { return mPump; }
+ const LLEventPump& getPump() const { return mPump; }
+
+ std::string getName() const { return mPump.getName(); }
+ void post(const LLSD& data) { mPump.post(data); }
+
+ LLEventStream mPump;
+ };
+
+ // Mimic a dummy little LLEventAPI that merely sends a reply back to its
+ // requester on the "reply" pump.
+ struct AckAPI: public ListenerBase
+ {
+ AckAPI(): ListenerBase("AckAPI") {}
+
+ virtual bool call(const LLSD& request)
+ {
+ LLEventPumps::instance().obtain(request["reply"]).post("ack");
+ return false;
+ }
+ };
+
+ // Give LLLeap script a way to post success/failure.
+ struct Result: public ListenerBase
+ {
+ Result(): ListenerBase("Result") {}
+
+ virtual bool call(const LLSD& request)
+ {
+ mData = request;
+ return false;
+ }
+
+ void ensure() const
+ {
+ tut::ensure(std::string("never posted to ") + getName(), mData.isDefined());
+ // Post an empty string for success, non-empty string is failure message.
+ tut::ensure(mData, mData.asString().empty());
+ }
+
+ LLSD mData;
+ };
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("round trip");
+ AckAPI api;
+ Result result;
+ NamedTempFile script("py",
+ boost::phoenix::placeholders::arg1 <<
+ "from " << reader_module << " import *\n"
+ // make a request on our little API
+ "request(pump='" << api.getName() << "', data={})\n"
+ // wait for its response
+ "resp = get()\n"
+ "result = '' if resp == dict(pump=replypump(), data='ack')\\\n"
+ " else 'bad: ' + str(resp)\n"
+ "send(pump='" << result.getName() << "', data=result)\n");
+ waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))));
+ result.ensure();
+ }
+
+ struct ReqIDAPI: public ListenerBase
+ {
+ ReqIDAPI(): ListenerBase("ReqIDAPI") {}
+
+ virtual bool call(const LLSD& request)
+ {
+ // free function from llevents.h
+ sendReply(LLSD(), request);
+ return false;
+ }
+ };
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("many small messages");
+ // It's not clear to me whether there's value in iterating many times
+ // over a send/receive loop -- I don't think that will exercise any
+ // interesting corner cases. This test first sends a large number of
+ // messages, then receives all the responses. The intent is to ensure
+ // that some of that data stream crosses buffer boundaries, loop
+ // iterations etc. in OS pipes and the LLLeap/LLProcess implementation.
+ ReqIDAPI api;
+ Result result;
+ NamedTempFile script("py",
+ boost::phoenix::placeholders::arg1 <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Note that since reader imports llsd, this
+ // 'import *' gets us llsd too.
+ "sample = llsd.format_notation(dict(pump='" <<
+ api.getName() << "', data=dict(reqid=999999, reply=replypump())))\n"
+ // The whole packet has length prefix too: "len:data"
+ "samplen = len(str(len(sample))) + 1 + len(sample)\n"
+ // guess how many messages it will take to
+ // accumulate BUFFERED_LENGTH
+ "count = int(" << BUFFERED_LENGTH << "/samplen)\n"
+ "print >>sys.stderr, 'Sending %s requests' % count\n"
+ "for i in xrange(count):\n"
+ " request('" << api.getName() << "', dict(reqid=i))\n"
+ // The assumption in this specific test that
+ // replies will arrive in the same order as
+ // requests is ONLY valid because the API we're
+ // invoking sends replies instantly. If the API
+ // had to wait for some external event before
+ // sending its reply, replies could arrive in
+ // arbitrary order, and we'd have to tick them
+ // off from a set.
+ "result = ''\n"
+ "for i in xrange(count):\n"
+ " resp = get()\n"
+ " if resp['data']['reqid'] != i:\n"
+ " result = 'expected reqid=%s in %s' % (i, resp)\n"
+ " break\n"
+ "send(pump='" << result.getName() << "', data=result)\n");
+ waitfor(LLLeap::create(get_test_name(), sv(list_of(PYTHON)(script.getName()))),
+ 300); // needs more realtime than most tests
+ result.ensure();
+ }
+
+ // This is the body of test<10>, extracted so we can run it over a number
+ // of large-message sizes.
+ void test_large_message(const std::string& PYTHON, const std::string& reader_module,
+ const std::string& test_name, size_t size)
+ {
+ ReqIDAPI api;
+ Result result;
+ NamedTempFile script("py",
+ boost::phoenix::placeholders::arg1 <<
+ "import sys\n"
+ "from " << reader_module << " import *\n"
+ // Generate a very large string value.
+ "desired = int(sys.argv[1])\n"
+ // 7 chars per item: 6 digits, 1 comma
+ "count = int((desired - 50)/7)\n"
+ "large = ''.join('%06d,' % i for i in xrange(count))\n"
+ // Pass 'large' as reqid because we know the API
+ // will echo reqid, and we want to receive it back.
+ "request('" << api.getName() << "', dict(reqid=large))\n"
+ "try:\n"
+ " resp = get()\n"
+ "except ParseError, e:\n"
+ " # try to find where e.data diverges from expectation\n"
+ // Normally we'd expect a 'pump' key in there,
+ // too, with value replypump(). But Python
+ // serializes keys in a different order than C++,
+ // so incoming data start with 'data'.
+ // Truthfully, though, if we get as far as 'pump'
+ // before we find a difference, something's very
+ // strange.
+ " expect = llsd.format_notation(dict(data=dict(reqid=large)))\n"
+ " chunk = 40\n"
+ " for offset in xrange(0, max(len(e.data), len(expect)), chunk):\n"
+ " if e.data[offset:offset+chunk] != \\\n"
+ " expect[offset:offset+chunk]:\n"
+ " print >>sys.stderr, 'Offset %06d: expect %r,\\n'\\\n"
+ " ' get %r' %\\\n"
+ " (offset,\n"
+ " expect[offset:offset+chunk],\n"
+ " e.data[offset:offset+chunk])\n"
+ " break\n"
+ " else:\n"
+ " print >>sys.stderr, 'incoming data matches expect?!'\n"
+ " send('" << result.getName() << "', '%s: %s' % (e.__class__.__name__, e))\n"
+ " sys.exit(1)\n"
+ "\n"
+ "echoed = resp['data']['reqid']\n"
+ "if echoed == large:\n"
+ " send('" << result.getName() << "', '')\n"
+ " sys.exit(0)\n"
+ // Here we know echoed did NOT match; try to find where
+ "for i in xrange(count):\n"
+ " start = 7*i\n"
+ " end = 7*(i+1)\n"
+ " if end > len(echoed)\\\n"
+ " or echoed[start:end] != large[start:end]:\n"
+ " send('" << result.getName() << "',\n"
+ " 'at offset %s, expected %r but got %r' %\n"
+ " (start, large[start:end], echoed[start:end]))\n"
+ "sys.exit(1)\n");
+ waitfor(LLLeap::create(test_name,
+ sv(list_of
+ (PYTHON)
+ (script.getName())
+ (stringize(size)))),
+ 180); // try a longer timeout
+ result.ensure();
+ }
+
+ struct TestLargeMessage: public std::binary_function<size_t, size_t, bool>
+ {
+ TestLargeMessage(const std::string& PYTHON_, const std::string& reader_module_,
+ const std::string& test_name_):
+ PYTHON(PYTHON_),
+ reader_module(reader_module_),
+ test_name(test_name_)
+ {}
+
+ bool operator()(size_t left, size_t right) const
+ {
+ // We don't know whether upper_bound is going to pass the "sought
+ // value" as the left or the right operand. We pass 0 as the
+ // "sought value" so we can distinguish it. Of course that means
+ // the sequence we're searching must not itself contain 0!
+ size_t size;
+ bool success;
+ if (left)
+ {
+ size = left;
+ // Consider our return value carefully. Normal binary_search
+ // (or, in our case, upper_bound) expects a container sorted
+ // in ascending order, and defaults to the std::less
+ // comparator. Our container is in fact in ascending order, so
+ // return consistently with std::less. Here we were called as
+ // compare(item, sought). If std::less were called that way,
+ // 'true' would mean to move right (to higher numbers) within
+ // the sequence: the item being considered is less than the
+ // sought value. For us, that means that test_large_message()
+ // success should return 'true'.
+ success = true;
+ }
+ else
+ {
+ size = right;
+ // Here we were called as compare(sought, item). If std::less
+ // were called that way, 'true' would mean to move left (to
+ // lower numbers) within the sequence: the sought value is
+ // less than the item being considered. For us, that means
+ // test_large_message() FAILURE should return 'true', hence
+ // test_large_message() success should return 'false'.
+ success = false;
+ }
+
+ try
+ {
+ test_large_message(PYTHON, reader_module, test_name, size);
+ std::cout << "test_large_message(" << size << ") succeeded" << std::endl;
+ return success;
+ }
+ catch (const failure& e)
+ {
+ std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
+ return ! success;
+ }
+ }
+
+ const std::string PYTHON, reader_module, test_name;
+ };
+
+ // The point of this function is to try to find a size at which
+ // test_large_message() can succeed. We still want the overall test to
+ // fail; otherwise we won't get the coder's attention -- but if
+ // test_large_message() fails, try to find a plausible size at which it
+ // DOES work.
+ void test_or_split(const std::string& PYTHON, const std::string& reader_module,
+ const std::string& test_name, size_t size)
+ {
+ try
+ {
+ test_large_message(PYTHON, reader_module, test_name, size);
+ }
+ catch (const failure& e)
+ {
+ std::cout << "test_large_message(" << size << ") failed: " << e.what() << std::endl;
+ // If it still fails below 4K, give up: subdividing any further is
+ // pointless.
+ if (size >= 4096)
+ {
+ try
+ {
+ // Recur with half the size
+ size_t smaller(size/2);
+ test_or_split(PYTHON, reader_module, test_name, smaller);
+ // Recursive call will throw if test_large_message()
+ // failed, therefore we only reach the line below if it
+ // succeeded.
+ std::cout << "but test_large_message(" << smaller << ") succeeded" << std::endl;
+
+ // Binary search for largest size that works. But since
+ // std::binary_search() only returns bool, actually use
+ // std::upper_bound(), consistent with our desire to find
+ // the LARGEST size that works. First generate a sorted
+ // container of all the sizes we intend to try, from
+ // 'smaller' (known to work) to 'size' (known to fail). We
+ // could whomp up magic iterators to do this dynamically,
+ // without actually instantiating a vector, but for a test
+ // program this will do. At least preallocate the vector.
+ // Per TestLargeMessage comments, it's important that this
+ // vector not contain 0.
+ std::vector<size_t> sizes;
+ sizes.reserve((size - smaller)/4096 + 1);
+ for (size_t sz(smaller), szend(size); sz < szend; sz += 4096)
+ sizes.push_back(sz);
+ // our comparator
+ TestLargeMessage tester(PYTHON, reader_module, test_name);
+ // Per TestLargeMessage comments, pass 0 as the sought value.
+ std::vector<size_t>::const_iterator found =
+ std::upper_bound(sizes.begin(), sizes.end(), 0, tester);
+ if (found != sizes.end() && found != sizes.begin())
+ {
+ std::cout << "test_large_message(" << *(found - 1)
+ << ") is largest that succeeds" << std::endl;
+ }
+ else
+ {
+ std::cout << "cannot determine largest test_large_message(size) "
+ << "that succeeds" << std::endl;
+ }
+ }
+ catch (const failure&)
+ {
+ // The recursive test_or_split() call above has already
+ // handled the exception. We don't want our caller to see
+ // innermost exception; propagate outermost (below).
+ }
+ }
+ // In any case, because we reached here through failure of
+ // our original test_large_message(size) call, ensure failure
+ // propagates.
+ throw e;
+ }
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("very large message");
+ test_or_split(PYTHON, reader_module, get_test_name(), BUFFERED_LENGTH);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llmemtype_test.cpp b/indra/llcommon/tests/llmemtype_test.cpp
index 6cc5ce01ce..1f050d6dc7 100644..100755
--- a/indra/llcommon/tests/llmemtype_test.cpp
+++ b/indra/llcommon/tests/llmemtype_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-03-
* @brief Test for llmemtype.cpp.
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
new file mode 100755
index 0000000000..5ba343b183
--- /dev/null
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -0,0 +1,1262 @@
+/**
+ * @file llprocess_test.cpp
+ * @author Nat Goodspeed
+ * @date 2011-12-19
+ * @brief Test for llprocess.
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Copyright (c) 2011, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llprocess.h"
+// STL headers
+#include <vector>
+#include <list>
+// std headers
+#include <fstream>
+// external library headers
+#include "llapr.h"
+#include "apr_thread_proc.h"
+#include <boost/foreach.hpp>
+#include <boost/function.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
+//#include <boost/lambda/lambda.hpp>
+//#include <boost/lambda/bind.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "../test/catch_and_store_what_in.h"
+#include "stringize.h"
+#include "llsdutil.h"
+#include "llevents.h"
+#include "wrapllerrs.h"
+
+#if defined(LL_WINDOWS)
+#define sleep(secs) _sleep((secs) * 1000)
+#define EOL "\r\n"
+#else
+#define EOL "\n"
+#include <sys/wait.h>
+#endif
+
+//namespace lambda = boost::lambda;
+ std::string apr_strerror_helper(apr_status_t rv)
+{
+ char errbuf[256];
+ apr_strerror(rv, errbuf, sizeof(errbuf));
+ return errbuf;
+}
+
+/*****************************************************************************
+* Helpers
+*****************************************************************************/
+
+#define ensure_equals_(left, right) \
+ ensure_equals(STRINGIZE(#left << " != " << #right), (left), (right))
+
+#define aprchk(expr) aprchk_(#expr, (expr))
+static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR_SUCCESS)
+{
+ tut::ensure_equals(STRINGIZE(call << " => " << rv << ": " << apr_strerror_helper
+ (rv)),
+ rv, expected);
+}
+
+/**
+ * Read specified file using std::getline(). It is assumed to be an error if
+ * the file is empty: don't use this function if that's an acceptable case.
+ * Last line will not end with '\n'; this is to facilitate the usual case of
+ * string compares with a single line of output.
+ * @param pathname The file to read.
+ * @param desc Optional description of the file for error message;
+ * defaults to "in <pathname>"
+ */
+static std::string readfile(const std::string& pathname, const std::string& desc="")
+{
+ std::string use_desc(desc);
+ if (use_desc.empty())
+ {
+ use_desc = STRINGIZE("in " << pathname);
+ }
+ std::ifstream inf(pathname.c_str());
+ std::string output;
+ tut::ensure(STRINGIZE("No output " << use_desc), bool(std::getline(inf, output)));
+ std::string more;
+ while (std::getline(inf, more))
+ {
+ output += '\n' + more;
+ }
+ return output;
+}
+
+/// Looping on LLProcess::isRunning() must now be accompanied by pumping
+/// "mainloop" -- otherwise the status won't update and you get an infinite
+/// loop.
+void yield(int seconds=1)
+{
+ // This function simulates waiting for another viewer frame
+ sleep(seconds);
+ LLEventPumps::instance().obtain("mainloop").post(LLSD());
+}
+
+void waitfor(LLProcess& proc, int timeout=60)
+{
+ int i = 0;
+ for ( ; i < timeout && proc.isRunning(); ++i)
+ {
+ yield();
+ }
+ tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
+ i < timeout);
+}
+
+void waitfor(LLProcess::handle h, const std::string& desc, int timeout=60)
+{
+ int i = 0;
+ for ( ; i < timeout && LLProcess::isRunning(h, desc); ++i)
+ {
+ yield();
+ }
+ tut::ensure(STRINGIZE("process took longer than " << timeout << " seconds to terminate"),
+ i < timeout);
+}
+
+/**
+ * Construct an LLProcess to run a Python script.
+ */
+struct PythonProcessLauncher
+{
+ /**
+ * @param desc Arbitrary description for error messages
+ * @param script Python script, any form acceptable to NamedTempFile,
+ * typically either a std::string or an expression of the form
+ * (lambda::_1 << "script content with " << variable_data)
+ */
+ template <typename CONTENT>
+ PythonProcessLauncher(const std::string& desc, const CONTENT& script):
+ mDesc(desc),
+ mScript("py", script)
+ {
+ const char* PYTHON(getenv("PYTHON"));
+ tut::ensure("Set $PYTHON to the Python interpreter", PYTHON);
+
+ mParams.desc = desc + " script";
+ mParams.executable = PYTHON;
+ mParams.args.add(mScript.getName());
+ }
+
+ /// Launch Python script; verify that it launched
+ void launch()
+ {
+ mPy = LLProcess::create(mParams);
+ tut::ensure(STRINGIZE("Couldn't launch " << mDesc << " script"), bool(mPy));
+ }
+
+ /// Run Python script and wait for it to complete.
+ void run()
+ {
+ launch();
+ // One of the irritating things about LLProcess is that
+ // there's no API to wait for the child to terminate -- but given
+ // its use in our graphics-intensive interactive viewer, it's
+ // understandable.
+ waitfor(*mPy);
+ }
+
+ /**
+ * Run a Python script using LLProcess, expecting that it will
+ * write to the file passed as its sys.argv[1]. Retrieve that output.
+ *
+ * Until January 2012, LLProcess provided distressingly few
+ * mechanisms for a child process to communicate back to its caller --
+ * not even its return code. We've introduced a convention by which we
+ * create an empty temp file, pass the name of that file to our child
+ * as sys.argv[1] and expect the script to write its output to that
+ * file. This function implements the C++ (parent process) side of
+ * that convention.
+ */
+ std::string run_read()
+ {
+ NamedTempFile out("out", ""); // placeholder
+ // pass name of this temporary file to the script
+ mParams.args.add(out.getName());
+ run();
+ // assuming the script wrote to that file, read it
+ return readfile(out.getName(), STRINGIZE("from " << mDesc << " script"));
+ }
+
+ LLProcess::Params mParams;
+ LLProcessPtr mPy;
+ std::string mDesc;
+ NamedTempFile mScript;
+};
+
+/// convenience function for PythonProcessLauncher::run()
+template <typename CONTENT>
+static void python(const std::string& desc, const CONTENT& script)
+{
+ PythonProcessLauncher py(desc, script);
+ py.run();
+}
+
+/// convenience function for PythonProcessLauncher::run_read()
+template <typename CONTENT>
+static std::string python_out(const std::string& desc, const CONTENT& script)
+{
+ PythonProcessLauncher py(desc, script);
+ return py.run_read();
+}
+
+/// Create a temporary directory and clean it up later.
+class NamedTempDir: public boost::noncopyable
+{
+public:
+ // Use python() function to create a temp directory: I've found
+ // nothing in either Boost.Filesystem or APR quite like Python's
+ // tempfile.mkdtemp().
+ // Special extra bonus: on Mac, mkdtemp() reports a pathname
+ // starting with /var/folders/something, whereas that's really a
+ // symlink to /private/var/folders/something. Have to use
+ // realpath() to compare properly.
+ NamedTempDir():
+ mPath(python_out("mkdtemp()",
+ "from __future__ import with_statement\n"
+ "import os.path, sys, tempfile\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write(os.path.normcase(os.path.normpath(os.path.realpath(tempfile.mkdtemp()))))\n"))
+ {}
+
+ ~NamedTempDir()
+ {
+ aprchk(apr_dir_remove(mPath.c_str(), gAPRPoolp));
+ }
+
+ std::string getName() const { return mPath; }
+
+private:
+ std::string mPath;
+};
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llprocess_data
+ {
+ LLAPRPool pool;
+ };
+ typedef test_group<llprocess_data> llprocess_group;
+ typedef llprocess_group::object object;
+ llprocess_group llprocessgrp("llprocess");
+
+ struct Item
+ {
+ Item(): tries(0) {}
+ unsigned tries;
+ std::string which;
+ std::string what;
+ };
+
+/*==========================================================================*|
+#define tabent(symbol) { symbol, #symbol }
+ static struct ReasonCode
+ {
+ int code;
+ const char* name;
+ } reasons[] =
+ {
+ tabent(APR_OC_REASON_DEATH),
+ tabent(APR_OC_REASON_UNWRITABLE),
+ tabent(APR_OC_REASON_RESTART),
+ tabent(APR_OC_REASON_UNREGISTER),
+ tabent(APR_OC_REASON_LOST),
+ tabent(APR_OC_REASON_RUNNING)
+ };
+#undef tabent
+|*==========================================================================*/
+
+ struct WaitInfo
+ {
+ WaitInfo(apr_proc_t* child_):
+ child(child_),
+ rv(-1), // we haven't yet called apr_proc_wait()
+ rc(0),
+ why(apr_exit_why_e(0))
+ {}
+ apr_proc_t* child; // which subprocess
+ apr_status_t rv; // return from apr_proc_wait()
+ int rc; // child's exit code
+ apr_exit_why_e why; // APR_PROC_EXIT, APR_PROC_SIGNAL, APR_PROC_SIGNAL_CORE
+ };
+
+ void child_status_callback(int reason, void* data, int status)
+ {
+/*==========================================================================*|
+ std::string reason_str;
+ BOOST_FOREACH(const ReasonCode& rcp, reasons)
+ {
+ if (reason == rcp.code)
+ {
+ reason_str = rcp.name;
+ break;
+ }
+ }
+ if (reason_str.empty())
+ {
+ reason_str = STRINGIZE("unknown reason " << reason);
+ }
+ std::cout << "child_status_callback(" << reason_str << ")\n";
+|*==========================================================================*/
+
+ if (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST)
+ {
+ // Somewhat oddly, APR requires that you explicitly unregister
+ // even when it already knows the child has terminated.
+ apr_proc_other_child_unregister(data);
+
+ WaitInfo* wi(static_cast<WaitInfo*>(data));
+ // It's just wrong to call apr_proc_wait() here. The only way APR
+ // knows to call us with APR_OC_REASON_DEATH is that it's already
+ // reaped this child process, so calling wait() will only produce
+ // "huh?" from the OS. We must rely on the status param passed in,
+ // which unfortunately comes straight from the OS wait() call.
+// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
+ wi->rv = APR_CHILD_DONE; // fake apr_proc_wait() results
+#if defined(LL_WINDOWS)
+ wi->why = APR_PROC_EXIT;
+ wi->rc = status; // no encoding on Windows (no signals)
+#else // Posix
+ if (WIFEXITED(status))
+ {
+ wi->why = APR_PROC_EXIT;
+ wi->rc = WEXITSTATUS(status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ wi->why = APR_PROC_SIGNAL;
+ wi->rc = WTERMSIG(status);
+ }
+ else // uh, shouldn't happen?
+ {
+ wi->why = APR_PROC_EXIT;
+ wi->rc = status; // someone else will have to decode
+ }
+#endif // Posix
+ }
+ }
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("raw APR nonblocking I/O");
+
+ // Create a script file in a temporary place.
+ NamedTempFile script("py",
+ "import sys" EOL
+ "import time" EOL
+ EOL
+ "time.sleep(2)" EOL
+ "print >>sys.stdout, 'stdout after wait'" EOL
+ "sys.stdout.flush()" EOL
+ "time.sleep(2)" EOL
+ "print >>sys.stderr, 'stderr after wait'" EOL
+ "sys.stderr.flush()" EOL
+ );
+
+ // Arrange to track the history of our interaction with child: what we
+ // fetched, which pipe it came from, how many tries it took before we
+ // got it.
+ std::vector<Item> history;
+ history.push_back(Item());
+
+ // Run the child process.
+ apr_procattr_t *procattr = NULL;
+ aprchk(apr_procattr_create(&procattr, pool.getAPRPool()));
+ aprchk(apr_procattr_io_set(procattr, APR_CHILD_BLOCK, APR_CHILD_BLOCK, APR_CHILD_BLOCK));
+ aprchk(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+
+ std::vector<const char*> argv;
+ apr_proc_t child;
+ argv.push_back("python");
+ // Have to have a named copy of this std::string so its c_str() value
+ // will persist.
+ std::string scriptname(script.getName());
+ argv.push_back(scriptname.c_str());
+ argv.push_back(NULL);
+
+ aprchk(apr_proc_create(&child, argv[0],
+ &argv[0],
+ NULL, // if we wanted to pass explicit environment
+ procattr,
+ pool.getAPRPool()));
+
+ // We do not want this child process to outlive our APR pool. On
+ // destruction of the pool, forcibly kill the process. Tell APR to try
+ // SIGTERM and wait 3 seconds. If that didn't work, use SIGKILL.
+ apr_pool_note_subprocess(pool.getAPRPool(), &child, APR_KILL_AFTER_TIMEOUT);
+
+ // arrange to call child_status_callback()
+ WaitInfo wi(&child);
+ apr_proc_other_child_register(&child, child_status_callback, &wi, child.in, pool.getAPRPool());
+
+ // TODO:
+ // Stuff child.in until it (would) block to verify EWOULDBLOCK/EAGAIN.
+ // Have child script clear it later, then write one more line to prove
+ // that it gets through.
+
+ // Monitor two different output pipes. Because one will be closed
+ // before the other, keep them in a list so we can drop whichever of
+ // them is closed first.
+ typedef std::pair<std::string, apr_file_t*> DescFile;
+ typedef std::list<DescFile> DescFileList;
+ DescFileList outfiles;
+ outfiles.push_back(DescFile("out", child.out));
+ outfiles.push_back(DescFile("err", child.err));
+
+ while (! outfiles.empty())
+ {
+ // This peculiar for loop is designed to let us erase(dfli). With
+ // a list, that invalidates only dfli itself -- but even so, we
+ // lose the ability to increment it for the next item. So at the
+ // top of every loop, while dfli is still valid, increment
+ // dflnext. Then before the next iteration, set dfli to dflnext.
+ for (DescFileList::iterator
+ dfli(outfiles.begin()), dflnext(outfiles.begin()), dflend(outfiles.end());
+ dfli != dflend; dfli = dflnext)
+ {
+ // Only valid to increment dflnext once we're sure it's not
+ // already at dflend.
+ ++dflnext;
+
+ char buf[4096];
+
+ apr_status_t rv = apr_file_gets(buf, sizeof(buf), dfli->second);
+ if (APR_STATUS_IS_EOF(rv))
+ {
+// std::cout << "(EOF on " << dfli->first << ")\n";
+// history.back().which = dfli->first;
+// history.back().what = "*eof*";
+// history.push_back(Item());
+ outfiles.erase(dfli);
+ continue;
+ }
+ if (rv == EWOULDBLOCK || rv == EAGAIN)
+ {
+// std::cout << "(waiting; apr_file_gets(" << dfli->first << ") => " << rv << ": " << manager.strerror(rv) << ")\n";
+ ++history.back().tries;
+ continue;
+ }
+ aprchk_("apr_file_gets(buf, sizeof(buf), dfli->second)", rv);
+ // Is it even possible to get APR_SUCCESS but read 0 bytes?
+ // Hope not, but defend against that anyway.
+ if (buf[0])
+ {
+// std::cout << dfli->first << ": " << buf;
+ history.back().which = dfli->first;
+ history.back().what.append(buf);
+ if (buf[strlen(buf) - 1] == '\n')
+ history.push_back(Item());
+ else
+ {
+ // Just for pretty output... if we only read a partial
+ // line, terminate it.
+// std::cout << "...\n";
+ }
+ }
+ }
+ // Do this once per tick, as we expect the viewer will
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ sleep(1);
+ }
+ apr_file_close(child.in);
+ apr_file_close(child.out);
+ apr_file_close(child.err);
+
+ // Okay, we've broken the loop because our pipes are all closed. If we
+ // haven't yet called wait, give the callback one more chance. This
+ // models the fact that unlike this small test program, the viewer
+ // will still be running.
+ if (wi.rv == -1)
+ {
+ std::cout << "last gasp apr_proc_other_child_refresh_all()\n";
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ }
+
+ if (wi.rv == -1)
+ {
+ std::cout << "child_status_callback(APR_OC_REASON_DEATH) wasn't called" << std::endl;
+ wi.rv = apr_proc_wait(wi.child, &wi.rc, &wi.why, APR_NOWAIT);
+ }
+// std::cout << "child done: rv = " << rv << " (" << manager.strerror(rv) << "), why = " << why << ", rc = " << rc << '\n';
+ aprchk_("apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT)", wi.rv, APR_CHILD_DONE);
+ ensure_equals_(wi.why, APR_PROC_EXIT);
+ ensure_equals_(wi.rc, 0);
+
+ // Beyond merely executing all the above successfully, verify that we
+ // obtained expected output -- and that we duly got control while
+ // waiting, proving the non-blocking nature of these pipes.
+ try
+ {
+ unsigned i = 0;
+ ensure("blocking I/O on child pipe (0)", history[i].tries);
+ ensure_equals_(history[i].which, "out");
+ ensure_equals_(history[i].what, "stdout after wait" EOL);
+// ++i;
+// ensure_equals_(history[i].which, "out");
+// ensure_equals_(history[i].what, "*eof*");
+ ++i;
+ ensure("blocking I/O on child pipe (1)", history[i].tries);
+ ensure_equals_(history[i].which, "err");
+ ensure_equals_(history[i].what, "stderr after wait" EOL);
+// ++i;
+// ensure_equals_(history[i].which, "err");
+// ensure_equals_(history[i].what, "*eof*");
+ }
+ catch (const failure&)
+ {
+ std::cout << "History:\n";
+ BOOST_FOREACH(const Item& item, history)
+ {
+ std::string what(item.what);
+ if ((! what.empty()) && what[what.length() - 1] == '\n')
+ {
+ what.erase(what.length() - 1);
+ if ((! what.empty()) && what[what.length() - 1] == '\r')
+ {
+ what.erase(what.length() - 1);
+ what.append("\\r");
+ }
+ what.append("\\n");
+ }
+ std::cout << " " << item.which << ": '" << what << "' ("
+ << item.tries << " tries)\n";
+ }
+ std::cout << std::flush;
+ // re-raise same error; just want to enrich the output
+ throw;
+ }
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("setWorkingDirectory()");
+ // We want to test setWorkingDirectory(). But what directory is
+ // guaranteed to exist on every machine, under every OS? Have to
+ // create one. Naturally, ensure we clean it up when done.
+ NamedTempDir tempdir;
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import os, sys\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write(os.path.normcase(os.path.normpath(os.getcwd())))\n");
+ // Before running, call setWorkingDirectory()
+ py.mParams.cwd = tempdir.getName();
+ ensure_equals("os.getcwd()", py.run_read(), tempdir.getName());
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("arguments");
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys\n"
+ // note nonstandard output-file arg!
+ "with open(sys.argv[3], 'w') as f:\n"
+ " for arg in sys.argv[1:]:\n"
+ " print >>f, arg\n");
+ // We expect that PythonProcessLauncher has already appended
+ // its own NamedTempFile to mParams.args (sys.argv[0]).
+ py.mParams.args.add("first arg"); // sys.argv[1]
+ py.mParams.args.add("second arg"); // sys.argv[2]
+ // run_read() appends() one more argument, hence [3]
+ std::string output(py.run_read());
+ boost::split_iterator<std::string::const_iterator>
+ li(output, boost::first_finder("\n")), lend;
+ ensure("didn't get first arg", li != lend);
+ std::string arg(li->begin(), li->end());
+ ensure_equals(arg, "first arg");
+ ++li;
+ ensure("didn't get second arg", li != lend);
+ arg.assign(li->begin(), li->end());
+ ensure_equals(arg, "second arg");
+ ++li;
+ ensure("didn't get output filename?!", li != lend);
+ arg.assign(li->begin(), li->end());
+ ensure("output filename empty?!", ! arg.empty());
+ ++li;
+ ensure("too many args", li == lend);
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("exit(0)");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(0)\n");
+ py.run();
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 0);
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("exit(2)");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(2)\n");
+ py.run();
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 2);
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("syntax_error");
+ PythonProcessLauncher py(get_test_name(),
+ "syntax_error:\n");
+ py.mParams.files.add(LLProcess::FileParam()); // inherit stdin
+ py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
+ py.mParams.files.add(LLProcess::FileParam().type("pipe")); // pipe for stderr
+ py.run();
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 1);
+ std::istream& rpipe(py.mPy->getReadPipe(LLProcess::STDERR).get_istream());
+ std::vector<char> buffer(4096);
+ rpipe.read(&buffer[0], buffer.size());
+ std::streamsize got(rpipe.gcount());
+ ensure("Nothing read from stderr pipe", got);
+ std::string data(&buffer[0], got);
+ ensure("Didn't find 'SyntaxError:'", data.find("\nSyntaxError:") != std::string::npos);
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("explicit kill()");
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys, time\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ok')\n"
+ "# now sleep; expect caller to kill\n"
+ "time.sleep(120)\n"
+ "# if caller hasn't managed to kill by now, bad\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('bad')\n");
+ NamedTempFile out("out", "not started");
+ py.mParams.args.add(out.getName());
+ py.launch();
+ // Wait for the script to wake up and do its first write
+ int i = 0, timeout = 60;
+ for ( ; i < timeout; ++i)
+ {
+ yield();
+ if (readfile(out.getName(), "from kill() script") == "ok")
+ break;
+ }
+ // If we broke this loop because of the counter, something's wrong
+ ensure("script never started", i < timeout);
+ // script has performed its first write and should now be sleeping.
+ py.mPy->kill();
+ // wait for the script to terminate... one way or another.
+ waitfor(*py.mPy);
+#if LL_WINDOWS
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, -1);
+#else
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::KILLED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, SIGTERM);
+#endif
+ // If kill() failed, the script would have woken up on its own and
+ // overwritten the file with 'bad'. But if kill() succeeded, it should
+ // not have had that chance.
+ ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("implicit kill()");
+ NamedTempFile out("out", "not started");
+ LLProcess::handle phandle(0);
+ {
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys, time\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ok')\n"
+ "# now sleep; expect caller to kill\n"
+ "time.sleep(120)\n"
+ "# if caller hasn't managed to kill by now, bad\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('bad')\n");
+ py.mParams.args.add(out.getName());
+ py.launch();
+ // Capture handle for later
+ phandle = py.mPy->getProcessHandle();
+ // Wait for the script to wake up and do its first write
+ int i = 0, timeout = 60;
+ for ( ; i < timeout; ++i)
+ {
+ yield();
+ if (readfile(out.getName(), "from kill() script") == "ok")
+ break;
+ }
+ // If we broke this loop because of the counter, something's wrong
+ ensure("script never started", i < timeout);
+ // Script has performed its first write and should now be sleeping.
+ // Destroy the LLProcess, which should kill the child.
+ }
+ // wait for the script to terminate... one way or another.
+ waitfor(phandle, "kill() script");
+ // If kill() failed, the script would have woken up on its own and
+ // overwritten the file with 'bad'. But if kill() succeeded, it should
+ // not have had that chance.
+ ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("autokill=false");
+ NamedTempFile from("from", "not started");
+ NamedTempFile to("to", "");
+ LLProcess::handle phandle(0);
+ {
+ PythonProcessLauncher py(get_test_name(),
+ "from __future__ import with_statement\n"
+ "import sys, time\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ok')\n"
+ "# wait for 'go' from test program\n"
+ "for i in xrange(60):\n"
+ " time.sleep(1)\n"
+ " with open(sys.argv[2]) as f:\n"
+ " go = f.read()\n"
+ " if go == 'go':\n"
+ " break\n"
+ "else:\n"
+ " with open(sys.argv[1], 'w') as f:\n"
+ " f.write('never saw go')\n"
+ " sys.exit(1)\n"
+ "# okay, saw 'go', write 'ack'\n"
+ "with open(sys.argv[1], 'w') as f:\n"
+ " f.write('ack')\n");
+ py.mParams.args.add(from.getName());
+ py.mParams.args.add(to.getName());
+ py.mParams.autokill = false;
+ py.launch();
+ // Capture handle for later
+ phandle = py.mPy->getProcessHandle();
+ // Wait for the script to wake up and do its first write
+ int i = 0, timeout = 60;
+ for ( ; i < timeout; ++i)
+ {
+ yield();
+ if (readfile(from.getName(), "from autokill script") == "ok")
+ break;
+ }
+ // If we broke this loop because of the counter, something's wrong
+ ensure("script never started", i < timeout);
+ // Now destroy the LLProcess, which should NOT kill the child!
+ }
+ // If the destructor killed the child anyway, give it time to die
+ yield(2);
+ // How do we know it's not terminated? By making it respond to
+ // a specific stimulus in a specific way.
+ {
+ std::ofstream outf(to.getName().c_str());
+ outf << "go";
+ } // flush and close.
+ // now wait for the script to terminate... one way or another.
+ waitfor(phandle, "autokill script");
+ // If the LLProcess destructor implicitly called kill(), the
+ // script could not have written 'ack' as we expect.
+ ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("'bogus' test");
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam("bogus"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'bogus'", ! py.mPy);
+ std::string message(recorder.messageWith("bogus"));
+ ensure_contains("did not name 'stdin'", message, "stdin");
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ set_test_name("'file' test");
+ // Replace this test with one or more real 'file' tests when we
+ // implement 'file' support
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam("file"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'file'", ! py.mPy);
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ set_test_name("'tpipe' test");
+ // Replace this test with one or more real 'tpipe' tests when we
+ // implement 'tpipe' support
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam("tpipe"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'tpipe'", ! py.mPy);
+ std::string message(recorder.messageWith("tpipe"));
+ ensure_contains("did not name 'stdout'", message, "stdout");
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ set_test_name("'npipe' test");
+ // Replace this test with one or more real 'npipe' tests when we
+ // implement 'npipe' support
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello world'\n");
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam());
+ py.mParams.files.add(LLProcess::FileParam("npipe"));
+ py.mPy = LLProcess::create(py.mParams);
+ ensure("should have rejected 'npipe'", ! py.mPy);
+ std::string message(recorder.messageWith("npipe"));
+ ensure_contains("did not name 'stderr'", message, "stderr");
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ set_test_name("internal pipe name warning");
+ CaptureLog recorder;
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(7)\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe", "somename"));
+ py.run(); // verify that it did launch anyway
+ ensure_equals("Status.mState", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("Status.mData", py.mPy->getStatus().mData, 7);
+ std::string message(recorder.messageWith("not yet supported"));
+ ensure_contains("log message did not mention internal pipe name",
+ message, "somename");
+ }
+
+ /*-------------- support for "get*Pipe() validation" test --------------*/
+#define TEST_getPipe(PROCESS, GETPIPE, GETOPTPIPE, VALID, NOPIPE, BADPIPE) \
+ do \
+ { \
+ std::string threw; \
+ /* Both the following calls should work. */ \
+ (PROCESS).GETPIPE(VALID); \
+ ensure(#GETOPTPIPE "(" #VALID ") failed", bool((PROCESS).GETOPTPIPE(VALID))); \
+ /* pass obviously bogus PIPESLOT */ \
+ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(LLProcess::FILESLOT(4))); \
+ ensure_contains("didn't reject bad slot", threw, "no slot"); \
+ ensure_contains("didn't mention bad slot num", threw, "4"); \
+ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(LLProcess::FILESLOT(4))); \
+ /* pass NOPIPE */ \
+ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(NOPIPE)); \
+ ensure_contains("didn't reject non-pipe", threw, "not a monitored"); \
+ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(NOPIPE)); \
+ /* pass BADPIPE: FILESLOT isn't empty but wrong direction */ \
+ CATCH_IN(threw, LLProcess::NoPipe, (PROCESS).GETPIPE(BADPIPE)); \
+ /* sneaky: GETPIPE is getReadPipe or getWritePipe */ \
+ /* so skip "get" to obtain ReadPipe or WritePipe :-P */ \
+ ensure_contains("didn't reject wrong pipe", threw, (#GETPIPE)+3); \
+ EXPECT_FAIL_WITH_LOG(threw, (PROCESS).GETOPTPIPE(BADPIPE)); \
+ } while (0)
+
+/// For expecting exceptions. Execute CODE, catch EXCEPTION, store its what()
+/// in std::string THREW, ensure it's not empty (i.e. EXCEPTION did happen).
+#define CATCH_IN(THREW, EXCEPTION, CODE) \
+ do \
+ { \
+ (THREW).clear(); \
+ try \
+ { \
+ CODE; \
+ } \
+ CATCH_AND_STORE_WHAT_IN(THREW, EXCEPTION) \
+ ensure("failed to throw " #EXCEPTION ": " #CODE, ! (THREW).empty()); \
+ } while (0)
+
+#define EXPECT_FAIL_WITH_LOG(EXPECT, CODE) \
+ do \
+ { \
+ CaptureLog recorder; \
+ ensure(#CODE " succeeded", ! (CODE)); \
+ recorder.messageWith(EXPECT); \
+ } while (0)
+
+ template<> template<>
+ void object::test<15>()
+ {
+ set_test_name("get*Pipe() validation");
+ PythonProcessLauncher py(get_test_name(),
+ "print 'this output is expected'\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin
+ py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
+ py.run();
+ TEST_getPipe(*py.mPy, getWritePipe, getOptWritePipe,
+ LLProcess::STDIN, // VALID
+ LLProcess::STDOUT, // NOPIPE
+ LLProcess::STDERR); // BADPIPE
+ TEST_getPipe(*py.mPy, getReadPipe, getOptReadPipe,
+ LLProcess::STDERR, // VALID
+ LLProcess::STDOUT, // NOPIPE
+ LLProcess::STDIN); // BADPIPE
+ }
+
+ template<> template<>
+ void object::test<16>()
+ {
+ set_test_name("talk to stdin/stdout");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys, time\n"
+ "print 'ok'\n"
+ "sys.stdout.flush()\n"
+ "# wait for 'go' from test program\n"
+ "go = sys.stdin.readline()\n"
+ "if go != 'go\\n':\n"
+ " sys.exit('expected \"go\", saw %r' % go)\n"
+ "print 'ack'\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ int i, timeout = 60;
+ for (i = 0; i < timeout && py.mPy->isRunning() && childout.size() < 3; ++i)
+ {
+ yield();
+ }
+ ensure("script never started", i < timeout);
+ ensure_equals("bad wakeup from stdin/stdout script",
+ childout.getline(), "ok");
+ // important to get the implicit flush from std::endl
+ py.mPy->getWritePipe().get_ostream() << "go" << std::endl;
+ waitfor(*py.mPy);
+ ensure("script never replied", childout.contains("\n"));
+ ensure_equals("child didn't ack", childout.getline(), "ack");
+ ensure_equals("bad child termination", py.mPy->getStatus().mState, LLProcess::EXITED);
+ ensure_equals("bad child exit code", py.mPy->getStatus().mData, 0);
+ }
+
+ struct EventListener: public boost::noncopyable
+ {
+ EventListener(LLEventPump& pump)
+ {
+ mConnection =
+ pump.listen("EventListener", boost::bind(&EventListener::tick, this, _1));
+ }
+
+ bool tick(const LLSD& data)
+ {
+ mHistory.push_back(data);
+ return false;
+ }
+
+ std::list<LLSD> mHistory;
+ LLTempBoundListener mConnection;
+ };
+
+ static bool ack(std::ostream& out, const LLSD& data)
+ {
+ out << "continue" << std::endl;
+ return false;
+ }
+
+ template<> template<>
+ void object::test<17>()
+ {
+ set_test_name("listen for ReadPipe events");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.stdout.write('abc')\n"
+ "sys.stdout.flush()\n"
+ "sys.stdin.readline()\n"
+ "sys.stdout.write('def')\n"
+ "sys.stdout.flush()\n"
+ "sys.stdin.readline()\n"
+ "sys.stdout.write('ghi\\n')\n"
+ "sys.stdout.flush()\n"
+ "sys.stdin.readline()\n"
+ "sys.stdout.write('second line\\n')\n");
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ std::ostream& childin(py.mPy->getWritePipe(LLProcess::STDIN).get_ostream());
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ // lift the default limit; allow event to carry (some of) the actual data
+ childout.setLimit(20);
+ // listen for incoming data on childout
+ EventListener listener(childout.getPump());
+ // also listen with a function that prompts the child to continue
+ // every time we see output
+ LLTempBoundListener connection(
+ childout.getPump().listen("ack", boost::bind(ack, boost::ref(childin), _1)));
+ int i, timeout = 60;
+ // wait through stuttering first line
+ for (i = 0; i < timeout && py.mPy->isRunning() && ! childout.contains("\n"); ++i)
+ {
+ yield();
+ }
+ ensure("couldn't get first line", i < timeout);
+ // disconnect from listener
+ listener.mConnection.disconnect();
+ // finish out the run
+ waitfor(*py.mPy);
+ // now verify history
+ std::list<LLSD>::const_iterator li(listener.mHistory.begin()),
+ lend(listener.mHistory.end());
+ ensure("no events", li != lend);
+ ensure_equals("history[0]", (*li)["data"].asString(), "abc");
+ ensure_equals("history[0] len", (*li)["len"].asInteger(), 3);
+ ++li;
+ ensure("only 1 event", li != lend);
+ ensure_equals("history[1]", (*li)["data"].asString(), "abcdef");
+ ensure_equals("history[0] len", (*li)["len"].asInteger(), 6);
+ ++li;
+ ensure("only 2 events", li != lend);
+ ensure_equals("history[2]", (*li)["data"].asString(), "abcdefghi" EOL);
+ ensure_equals("history[0] len", (*li)["len"].asInteger(), 9 + sizeof(EOL) - 1);
+ ++li;
+ // We DO NOT expect a whole new event for the second line because we
+ // disconnected.
+ ensure("more than 3 events", li == lend);
+ }
+
+ template<> template<>
+ void object::test<18>()
+ {
+ set_test_name("ReadPipe \"eof\" event");
+ PythonProcessLauncher py(get_test_name(),
+ "print 'Hello from Python!'\n");
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ EventListener listener(childout.getPump());
+ waitfor(*py.mPy);
+ // We can't be positive there will only be a single event, if the OS
+ // (or any other intervening layer) does crazy buffering. What we want
+ // to ensure is that there was exactly ONE event with "eof" true, and
+ // that it was the LAST event.
+ std::list<LLSD>::const_reverse_iterator rli(listener.mHistory.rbegin()),
+ rlend(listener.mHistory.rend());
+ ensure("no events", rli != rlend);
+ ensure("last event not \"eof\"", (*rli)["eof"].asBoolean());
+ while (++rli != rlend)
+ {
+ ensure("\"eof\" event not last", ! (*rli)["eof"].asBoolean());
+ }
+ }
+
+ template<> template<>
+ void object::test<19>()
+ {
+ set_test_name("setLimit()");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.stdout.write(sys.argv[1])\n");
+ std::string abc("abcdefghijklmnopqrstuvwxyz");
+ py.mParams.args.add(abc);
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ // listen for incoming data on childout
+ EventListener listener(childout.getPump());
+ // but set limit
+ childout.setLimit(10);
+ ensure_equals("getLimit() after setlimit(10)", childout.getLimit(), 10);
+ // okay, pump I/O to pick up output from child
+ waitfor(*py.mPy);
+ ensure("no events", ! listener.mHistory.empty());
+ // For all we know, that data could have arrived in several different
+ // bursts... probably not, but anyway, only check the last one.
+ ensure_equals("event[\"len\"]",
+ listener.mHistory.back()["len"].asInteger(), abc.length());
+ ensure_equals("length of setLimit(10) data",
+ listener.mHistory.back()["data"].asString().length(), 10);
+ }
+
+ template<> template<>
+ void object::test<20>()
+ {
+ set_test_name("peek() ReadPipe data");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.stdout.write(sys.argv[1])\n");
+ std::string abc("abcdefghijklmnopqrstuvwxyz");
+ py.mParams.args.add(abc);
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.launch();
+ LLProcess::ReadPipe& childout(py.mPy->getReadPipe(LLProcess::STDOUT));
+ // okay, pump I/O to pick up output from child
+ waitfor(*py.mPy);
+ // peek() with substr args
+ ensure_equals("peek()", childout.peek(), abc);
+ ensure_equals("peek(23)", childout.peek(23), abc.substr(23));
+ ensure_equals("peek(5, 3)", childout.peek(5, 3), abc.substr(5, 3));
+ ensure_equals("peek(27, 2)", childout.peek(27, 2), "");
+ ensure_equals("peek(23, 5)", childout.peek(23, 5), "xyz");
+ // contains() -- we don't exercise as thoroughly as find() because the
+ // contains() implementation is trivially (and visibly) based on find()
+ ensure("contains(\":\")", ! childout.contains(":"));
+ ensure("contains(':')", ! childout.contains(':'));
+ ensure("contains(\"d\")", childout.contains("d"));
+ ensure("contains('d')", childout.contains('d'));
+ ensure("contains(\"klm\")", childout.contains("klm"));
+ ensure("contains(\"klx\")", ! childout.contains("klx"));
+ // find()
+ ensure("find(\":\")", childout.find(":") == LLProcess::ReadPipe::npos);
+ ensure("find(':')", childout.find(':') == LLProcess::ReadPipe::npos);
+ ensure_equals("find(\"d\")", childout.find("d"), 3);
+ ensure_equals("find('d')", childout.find('d'), 3);
+ ensure_equals("find(\"d\", 3)", childout.find("d", 3), 3);
+ ensure_equals("find('d', 3)", childout.find('d', 3), 3);
+ ensure("find(\"d\", 4)", childout.find("d", 4) == LLProcess::ReadPipe::npos);
+ ensure("find('d', 4)", childout.find('d', 4) == LLProcess::ReadPipe::npos);
+ // The case of offset == end and offset > end are different. In the
+ // first case, we can form a valid (albeit empty) iterator range and
+ // search that. In the second, guard logic in the implementation must
+ // realize we can't form a valid iterator range.
+ ensure("find(\"d\", 26)", childout.find("d", 26) == LLProcess::ReadPipe::npos);
+ ensure("find('d', 26)", childout.find('d', 26) == LLProcess::ReadPipe::npos);
+ ensure("find(\"d\", 27)", childout.find("d", 27) == LLProcess::ReadPipe::npos);
+ ensure("find('d', 27)", childout.find('d', 27) == LLProcess::ReadPipe::npos);
+ ensure_equals("find(\"ghi\")", childout.find("ghi"), 6);
+ ensure_equals("find(\"ghi\", 6)", childout.find("ghi"), 6);
+ ensure("find(\"ghi\", 7)", childout.find("ghi", 7) == LLProcess::ReadPipe::npos);
+ ensure("find(\"ghi\", 26)", childout.find("ghi", 26) == LLProcess::ReadPipe::npos);
+ ensure("find(\"ghi\", 27)", childout.find("ghi", 27) == LLProcess::ReadPipe::npos);
+ }
+
+ template<> template<>
+ void object::test<21>()
+ {
+ set_test_name("bad postend");
+ std::string pumpname("postend");
+ EventListener listener(LLEventPumps::instance().obtain(pumpname));
+ LLProcess::Params params;
+ params.desc = get_test_name();
+ params.postend = pumpname;
+ LLProcessPtr child = LLProcess::create(params);
+ ensure("shouldn't have launched", ! child);
+ ensure_equals("number of postend events", listener.mHistory.size(), 1);
+ LLSD postend(listener.mHistory.front());
+ ensure("has id", ! postend.has("id"));
+ ensure_equals("desc", postend["desc"].asString(), std::string(params.desc));
+ ensure_equals("state", postend["state"].asInteger(), LLProcess::UNSTARTED);
+ ensure("has data", ! postend.has("data"));
+ std::string error(postend["string"]);
+ // All we get from canned parameter validation is a bool, so the
+ // "validation failed" message we ourselves generate can't mention
+ // "executable" by name. Just check that it's nonempty.
+ //ensure_contains("error", error, "executable");
+ ensure("string", ! error.empty());
+ }
+
+ template<> template<>
+ void object::test<22>()
+ {
+ set_test_name("good postend");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ "sys.exit(35)\n");
+ std::string pumpname("postend");
+ EventListener listener(LLEventPumps::instance().obtain(pumpname));
+ py.mParams.postend = pumpname;
+ py.launch();
+ LLProcess::id childid(py.mPy->getProcessID());
+ // Don't use waitfor(), which calls isRunning(); instead wait for an
+ // event on pumpname.
+ int i, timeout = 60;
+ for (i = 0; i < timeout && listener.mHistory.empty(); ++i)
+ {
+ yield();
+ }
+ ensure("no postend event", i < timeout);
+ ensure_equals("number of postend events", listener.mHistory.size(), 1);
+ LLSD postend(listener.mHistory.front());
+ ensure_equals("id", postend["id"].asInteger(), childid);
+ ensure("desc empty", ! postend["desc"].asString().empty());
+ ensure_equals("state", postend["state"].asInteger(), LLProcess::EXITED);
+ ensure_equals("data", postend["data"].asInteger(), 35);
+ std::string str(postend["string"]);
+ ensure_contains("string", str, "exited");
+ ensure_contains("string", str, "35");
+ }
+
+ struct PostendListener
+ {
+ PostendListener(LLProcess::ReadPipe& rpipe,
+ const std::string& pumpname,
+ const std::string& expect):
+ mReadPipe(rpipe),
+ mExpect(expect),
+ mTriggered(false)
+ {
+ LLEventPumps::instance().obtain(pumpname)
+ .listen("PostendListener", boost::bind(&PostendListener::postend, this, _1));
+ }
+
+ bool postend(const LLSD&)
+ {
+ mTriggered = true;
+ ensure_equals("postend listener", mReadPipe.read(mReadPipe.size()), mExpect);
+ return false;
+ }
+
+ LLProcess::ReadPipe& mReadPipe;
+ std::string mExpect;
+ bool mTriggered;
+ };
+
+ template<> template<>
+ void object::test<23>()
+ {
+ set_test_name("all data visible at postend");
+ PythonProcessLauncher py(get_test_name(),
+ "import sys\n"
+ // note, no '\n' in written data
+ "sys.stdout.write('partial line')\n");
+ std::string pumpname("postend");
+ py.mParams.files.add(LLProcess::FileParam()); // stdin
+ py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
+ py.mParams.postend = pumpname;
+ py.launch();
+ PostendListener listener(py.mPy->getReadPipe(LLProcess::STDOUT),
+ pumpname,
+ "partial line");
+ waitfor(*py.mPy);
+ ensure("postend never triggered", listener.mTriggered);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llprocessor_test.cpp b/indra/llcommon/tests/llprocessor_test.cpp
new file mode 100755
index 0000000000..884e1b5e5b
--- /dev/null
+++ b/indra/llcommon/tests/llprocessor_test.cpp
@@ -0,0 +1,61 @@
+/**
+ * @file llprocessor_test.cpp
+ * @date 2010-06-01
+ *
+ * $LicenseInfo:firstyear=2010&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "../test/lltut.h"
+
+#include "../llprocessor.h"
+
+
+namespace tut
+{
+ struct processor
+ {
+ };
+
+ typedef test_group<processor> processor_t;
+ typedef processor_t::object processor_object_t;
+ tut::processor_t tut_processor("LLProcessor");
+
+ template<> template<>
+ void processor_object_t::test<1>()
+ {
+ set_test_name("LLProcessorInfo regression test");
+
+ LLProcessorInfo pi;
+ F64 freq = pi.getCPUFrequency();
+ //bool sse = pi.hasSSE();
+ //bool sse2 = pi.hasSSE2();
+ //bool alitvec = pi.hasAltivec();
+ std::string family = pi.getCPUFamilyName();
+ std::string brand = pi.getCPUBrandName();
+ //std::string steam = pi.getCPUFeatureDescription();
+
+ ensure_not_equals("Unknown Brand name", brand, "Unknown");
+ ensure_not_equals("Unknown Family name", family, "Unknown");
+ ensure("Reasonable CPU Frequency > 100 && < 10000", freq > 100 && freq < 10000);
+ }
+}
diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp
new file mode 100644
index 0000000000..12d5a695ee
--- /dev/null
+++ b/indra/llcommon/tests/llprocinfo_test.cpp
@@ -0,0 +1,91 @@
+/**
+ * @file llprocinfo_test.cpp
+ * @brief Tests for the LLProcInfo class.
+ *
+ * $LicenseInfo:firstyear=2013&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "../llprocinfo.h"
+
+#include "../test/lltut.h"
+#include "../lltimer.h"
+
+
+static const LLProcInfo::time_type bad_user(289375U), bad_system(275U);
+
+
+namespace tut
+{
+
+struct procinfo_test
+{
+ procinfo_test()
+ {
+ }
+};
+
+typedef test_group<procinfo_test> procinfo_group_t;
+typedef procinfo_group_t::object procinfo_object_t;
+tut::procinfo_group_t procinfo_instance("LLProcInfo");
+
+
+// Basic invocation works
+template<> template<>
+void procinfo_object_t::test<1>()
+{
+ LLProcInfo::time_type user(bad_user), system(bad_system);
+
+ set_test_name("getCPUUsage() basic function");
+
+ LLProcInfo::getCPUUsage(user, system);
+
+ ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user);
+ ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system);
+}
+
+
+// Time increases
+template<> template<>
+void procinfo_object_t::test<2>()
+{
+ LLProcInfo::time_type user(bad_user), system(bad_system);
+ LLProcInfo::time_type user2(bad_user), system2(bad_system);
+
+ set_test_name("getCPUUsage() increases over time");
+
+ LLProcInfo::getCPUUsage(user, system);
+
+ for (int i(0); i < 100000; ++i)
+ {
+ ms_sleep(0);
+ }
+
+ LLProcInfo::getCPUUsage(user2, system2);
+
+ ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true);
+ ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true);
+}
+
+
+} // end namespace tut
diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp
index 1f178d6fc9..383e6f9e0a 100644..100755
--- a/indra/llcommon/tests/llrand_test.cpp
+++ b/indra/llcommon/tests/llrand_test.cpp
@@ -3,31 +3,25 @@
* @author Phoenix
* @date 2007-01-25
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -45,7 +39,7 @@ namespace tut
typedef test_group<random> random_t;
typedef random_t::object random_object_t;
- tut::random_t tut_random("random");
+ tut::random_t tut_random("LLSeedRand");
template<> template<>
void random_object_t::test<1>()
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index 6ab48ec34a..6fbb9abfc0 100644..100755
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -3,61 +3,66 @@
* @date 2006-04
* @brief LLSDSerialize unit tests
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
-#if !LL_WINDOWS
+
+#include "linden_common.h"
+
+#if LL_WINDOWS
+#include <winsock2.h>
+typedef U32 uint32_t;
+#include <process.h>
+#include <io.h>
+#else
+#include <unistd.h>
#include <netinet/in.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include "llprocess.h"
#endif
-#include "linden_common.h"
+#include "boost/range.hpp"
+#include "boost/foreach.hpp"
+#include "boost/function.hpp"
+#include "boost/bind.hpp"
+#include "boost/phoenix/bind/bind_function.hpp"
+#include "boost/phoenix/core/argument.hpp"
+using namespace boost::phoenix;
+
#include "../llsd.h"
#include "../llsdserialize.h"
+#include "llsdutil.h"
#include "../llformat.h"
#include "../test/lltut.h"
+#include "../test/namedtempfile.h"
+#include "stringize.h"
-
-#if LL_WINDOWS
-#include <winsock2.h>
-typedef U32 uint32_t;
-#endif
-
-std::vector<U8> string_to_vector(std::string str)
+std::vector<U8> string_to_vector(const std::string& str)
{
- // bc LLSD can't...
- size_t len = (size_t)str.length();
- std::vector<U8> v(len);
- for (size_t i = 0; i < len ; i++)
- {
- v[i] = str[i];
- }
- return v;
+ return std::vector<U8>(str.begin(), str.end());
}
namespace tut
@@ -80,7 +85,7 @@ namespace tut
typedef test_group<sd_xml_data> sd_xml_test;
typedef sd_xml_test::object sd_xml_object;
- tut::sd_xml_test sd_xml_stream("sd_xml_serialization");
+ tut::sd_xml_test sd_xml_stream("LLSDXMLFormatter");
template<> template<>
void sd_xml_object::test<1>()
@@ -261,7 +266,7 @@ namespace tut
{
std::stringstream stream;
mFormatter->format(v, stream);
- //llinfos << "checkRoundTrip: length " << stream.str().length() << llendl;
+ //LL_INFOS() << "checkRoundTrip: length " << stream.str().length() << LL_ENDL;
LLSD w;
mParser->reset(); // reset() call is needed since test code re-uses mParser
mParser->parse(stream, w, stream.str().size());
@@ -458,7 +463,7 @@ namespace tut
checkRoundTrip(msg + " nested arrays", v);
v = LLSD::emptyMap();
- fillmap(v, 10, 6); // 10^6 maps
+ fillmap(v, 10, 3); // 10^6 maps
checkRoundTrip(msg + " many nested maps", v);
}
@@ -1500,5 +1505,226 @@ namespace tut
ensureBinaryAndNotation("map", test);
ensureBinaryAndXML("map", test);
}
-}
+ struct TestPythonCompatible
+ {
+ TestPythonCompatible():
+ // Note the peculiar insertion of __FILE__ into this string. Since
+ // this script is being written into a platform-dependent temp
+ // directory, we can't locate indra/lib/python relative to
+ // Python's __file__. Use __FILE__ instead, navigating relative
+ // to this C++ source file. Use Python raw-string syntax so
+ // Windows pathname backslashes won't mislead Python's string
+ // scanner.
+ import_llsd("import os.path\n"
+ "import sys\n"
+ "sys.path.insert(0,\n"
+ " os.path.join(os.path.dirname(r'" __FILE__ "'),\n"
+ " os.pardir, os.pardir, 'lib', 'python'))\n"
+ "from indra.base import llsd\n")
+ {}
+ ~TestPythonCompatible() {}
+
+ std::string import_llsd;
+
+ template <typename CONTENT>
+ void python(const std::string& desc, const CONTENT& script, int expect=0)
+ {
+ const char* PYTHON(getenv("PYTHON"));
+ ensure("Set $PYTHON to the Python interpreter", PYTHON);
+
+ NamedTempFile scriptfile("py", script);
+
+#if LL_WINDOWS
+ std::string q("\"");
+ std::string qPYTHON(q + PYTHON + q);
+ std::string qscript(q + scriptfile.getName() + q);
+ int rc = _spawnl(_P_WAIT, PYTHON, qPYTHON.c_str(), qscript.c_str(), NULL);
+ if (rc == -1)
+ {
+ char buffer[256];
+ strerror_s(buffer, errno); // C++ can infer the buffer size! :-O
+ ensure(STRINGIZE("Couldn't run Python " << desc << "script: " << buffer), false);
+ }
+ else
+ {
+ ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc), rc, expect);
+ }
+
+#else // LL_DARWIN, LL_LINUX
+ LLProcess::Params params;
+ params.executable = PYTHON;
+ params.args.add(scriptfile.getName());
+ LLProcessPtr py(LLProcess::create(params));
+ ensure(STRINGIZE("Couldn't launch " << desc << " script"), py);
+ // Implementing timeout would mean messing with alarm() and
+ // catching SIGALRM... later maybe...
+ int status(0);
+ if (waitpid(py->getProcessID(), &status, 0) == -1)
+ {
+ int waitpid_errno(errno);
+ ensure_equals(STRINGIZE("Couldn't retrieve rc from " << desc << " script: "
+ "waitpid() errno " << waitpid_errno),
+ waitpid_errno, ECHILD);
+ }
+ else
+ {
+ if (WIFEXITED(status))
+ {
+ int rc(WEXITSTATUS(status));
+ ensure_equals(STRINGIZE(desc << " script terminated with rc " << rc),
+ rc, expect);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ ensure(STRINGIZE(desc << " script terminated by signal " << WTERMSIG(status)),
+ false);
+ }
+ else
+ {
+ ensure(STRINGIZE(desc << " script produced impossible status " << status),
+ false);
+ }
+ }
+#endif
+ }
+ };
+
+ typedef tut::test_group<TestPythonCompatible> TestPythonCompatibleGroup;
+ typedef TestPythonCompatibleGroup::object TestPythonCompatibleObject;
+ TestPythonCompatibleGroup pycompat("LLSD serialize Python compatibility");
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<1>()
+ {
+ set_test_name("verify python()");
+ python("hello",
+ "import sys\n"
+ "sys.exit(17)\n",
+ 17); // expect nonzero rc
+ }
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<2>()
+ {
+ set_test_name("verify NamedTempFile");
+ python("platform",
+ "import sys\n"
+ "print 'Running on', sys.platform\n");
+ }
+
+ // helper for test<3>
+ static void writeLLSDArray(std::ostream& out, const LLSD& array)
+ {
+ BOOST_FOREACH(LLSD item, llsd::inArray(array))
+ {
+ LLSDSerialize::toNotation(item, out);
+ // It's important to separate with newlines because Python's llsd
+ // module doesn't support parsing from a file stream, only from a
+ // string, so we have to know how much of the file to read into a
+ // string.
+ out << '\n';
+ }
+ }
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<3>()
+ {
+ set_test_name("verify sequence to Python");
+
+ LLSD cdata(LLSDArray(17)(3.14)
+ ("This string\n"
+ "has several\n"
+ "lines."));
+
+ const char pydata[] =
+ "def verify(iterable):\n"
+ " it = iter(iterable)\n"
+ " assert it.next() == 17\n"
+ " assert abs(it.next() - 3.14) < 0.01\n"
+ " assert it.next() == '''\\\n"
+ "This string\n"
+ "has several\n"
+ "lines.'''\n"
+ " try:\n"
+ " it.next()\n"
+ " except StopIteration:\n"
+ " pass\n"
+ " else:\n"
+ " assert False, 'Too many data items'\n";
+
+ // Create an llsdXXXXXX file containing 'data' serialized to
+ // notation.
+ NamedTempFile file("llsd",
+ // NamedTempFile's boost::function constructor
+ // takes a callable. To this callable it passes the
+ // std::ostream with which it's writing the
+ // NamedTempFile.
+ boost::bind(writeLLSDArray, _1, cdata));
+
+ python("read C++ notation",
+ placeholders::arg1 <<
+ import_llsd <<
+ "def parse_each(iterable):\n"
+ " for item in iterable:\n"
+ " yield llsd.parse(item)\n" <<
+ pydata <<
+ // Don't forget raw-string syntax for Windows pathnames.
+ "verify(parse_each(open(r'" << file.getName() << "')))\n");
+ }
+
+ template<> template<>
+ void TestPythonCompatibleObject::test<4>()
+ {
+ set_test_name("verify sequence from Python");
+
+ // Create an empty data file. This is just a placeholder for our
+ // script to write into. Create it to establish a unique name that
+ // we know.
+ NamedTempFile file("llsd", "");
+
+ python("write Python notation",
+ placeholders::arg1 <<
+ "from __future__ import with_statement\n" <<
+ import_llsd <<
+ "DATA = [\n"
+ " 17,\n"
+ " 3.14,\n"
+ " '''\\\n"
+ "This string\n"
+ "has several\n"
+ "lines.''',\n"
+ "]\n"
+ // Don't forget raw-string syntax for Windows pathnames.
+ // N.B. Using 'print' implicitly adds newlines.
+ "with open(r'" << file.getName() << "', 'w') as f:\n"
+ " for item in DATA:\n"
+ " print >>f, llsd.format_notation(item)\n");
+
+ std::ifstream inf(file.getName().c_str());
+ LLSD item;
+ // Notice that we're not doing anything special to parse out the
+ // newlines: LLSDSerialize::fromNotation ignores them. While it would
+ // seem they're not strictly necessary, going in this direction, we
+ // want to ensure that notation-separated-by-newlines works in both
+ // directions -- since in practice, a given file might be read by
+ // either language.
+ ensure_equals("Failed to read LLSD::Integer from Python",
+ LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+ 1);
+ ensure_equals(item.asInteger(), 17);
+ ensure_equals("Failed to read LLSD::Real from Python",
+ LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+ 1);
+ ensure_approximately_equals("Bad LLSD::Real value from Python",
+ item.asReal(), 3.14, 7); // 7 bits ~= 0.01
+ ensure_equals("Failed to read LLSD::String from Python",
+ LLSDSerialize::fromNotation(item, inf, LLSDSerialize::SIZE_UNLIMITED),
+ 1);
+ ensure_equals(item.asString(),
+ "This string\n"
+ "has several\n"
+ "lines.");
+
+ }
+}
diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp
new file mode 100755
index 0000000000..385289aefe
--- /dev/null
+++ b/indra/llcommon/tests/llsingleton_test.cpp
@@ -0,0 +1,76 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llsingleton.h"
+#include "../test/lltut.h"
+
+namespace tut
+{
+ struct singleton
+ {
+ // We need a class created with the LLSingleton template to test with.
+ class LLSingletonTest: public LLSingleton<LLSingletonTest>
+ {
+
+ };
+ };
+
+ typedef test_group<singleton> singleton_t;
+ typedef singleton_t::object singleton_object_t;
+ tut::singleton_t tut_singleton("LLSingleton");
+
+ template<> template<>
+ void singleton_object_t::test<1>()
+ {
+
+ }
+ template<> template<>
+ void singleton_object_t::test<2>()
+ {
+ LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
+ ensure(singleton_test);
+ }
+ template<> template<>
+ void singleton_object_t::test<3>()
+ {
+ //Construct the instance
+ LLSingletonTest::getInstance();
+ ensure(LLSingletonTest::instanceExists());
+
+ //Delete the instance
+ LLSingletonTest::deleteSingleton();
+ ensure(LLSingletonTest::destroyed());
+ ensure(!LLSingletonTest::instanceExists());
+
+ //Construct it again.
+ LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
+ ensure(singleton_test);
+ ensure(LLSingletonTest::instanceExists());
+ }
+}
diff --git a/indra/llcommon/tests/llstreamqueue_test.cpp b/indra/llcommon/tests/llstreamqueue_test.cpp
new file mode 100755
index 0000000000..050ad5c5bf
--- /dev/null
+++ b/indra/llcommon/tests/llstreamqueue_test.cpp
@@ -0,0 +1,197 @@
+/**
+ * @file llstreamqueue_test.cpp
+ * @author Nat Goodspeed
+ * @date 2012-01-05
+ * @brief Test for llstreamqueue.
+ *
+ * $LicenseInfo:firstyear=2012&license=viewerlgpl$
+ * Copyright (c) 2012, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llstreamqueue.h"
+// STL headers
+#include <vector>
+// std headers
+// external library headers
+#include <boost/foreach.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+#include "stringize.h"
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llstreamqueue_data
+ {
+ llstreamqueue_data():
+ // we want a buffer with actual bytes in it, not an empty vector
+ buffer(10)
+ {}
+ // As LLStreamQueue is merely a typedef for
+ // LLGenericStreamQueue<char>, and no logic in LLGenericStreamQueue is
+ // specific to the <char> instantiation, we're comfortable for now
+ // testing only the narrow-char version.
+ LLStreamQueue strq;
+ // buffer for use in multiple tests
+ std::vector<char> buffer;
+ };
+ typedef test_group<llstreamqueue_data> llstreamqueue_group;
+ typedef llstreamqueue_group::object object;
+ llstreamqueue_group llstreamqueuegrp("llstreamqueue");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("empty LLStreamQueue");
+ ensure_equals("brand-new LLStreamQueue isn't empty",
+ strq.size(), 0);
+ ensure_equals("brand-new LLStreamQueue returns data",
+ strq.asSource().read(&buffer[0], buffer.size()), 0);
+ strq.asSink().close();
+ ensure_equals("closed empty LLStreamQueue not at EOF",
+ strq.asSource().read(&buffer[0], buffer.size()), -1);
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("one internal block, one buffer");
+ LLStreamQueue::Sink sink(strq.asSink());
+ ensure_equals("write(\"\")", sink.write("", 0), 0);
+ ensure_equals("0 write should leave LLStreamQueue empty (size())",
+ strq.size(), 0);
+ ensure_equals("0 write should leave LLStreamQueue empty (peek())",
+ strq.peek(&buffer[0], buffer.size()), 0);
+ // The meaning of "atomic" is that it must be smaller than our buffer.
+ std::string atomic("atomic");
+ ensure("test data exceeds buffer", atomic.length() < buffer.size());
+ ensure_equals(STRINGIZE("write(\"" << atomic << "\")"),
+ sink.write(&atomic[0], atomic.length()), atomic.length());
+ ensure_equals("size() after write()", strq.size(), atomic.length());
+ size_t peeklen(strq.peek(&buffer[0], buffer.size()));
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\")"),
+ peeklen, atomic.length());
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\") result"),
+ std::string(buffer.begin(), buffer.begin() + peeklen), atomic);
+ ensure_equals("size() after peek()", strq.size(), atomic.length());
+ // peek() should not consume. Use a different buffer to prove it isn't
+ // just leftover data from the first peek().
+ std::vector<char> again(buffer.size());
+ peeklen = size_t(strq.peek(&again[0], again.size()));
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again"),
+ peeklen, atomic.length());
+ ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again result"),
+ std::string(again.begin(), again.begin() + peeklen), atomic);
+ // now consume.
+ std::vector<char> third(buffer.size());
+ size_t readlen(strq.read(&third[0], third.size()));
+ ensure_equals(STRINGIZE("read(\"" << atomic << "\")"),
+ readlen, atomic.length());
+ ensure_equals(STRINGIZE("read(\"" << atomic << "\") result"),
+ std::string(third.begin(), third.begin() + readlen), atomic);
+ ensure_equals("peek() after read()", strq.peek(&buffer[0], buffer.size()), 0);
+ ensure_equals("size() after read()", strq.size(), 0);
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("basic skip()");
+ std::string lovecraft("lovecraft");
+ ensure("test data exceeds buffer", lovecraft.length() < buffer.size());
+ ensure_equals(STRINGIZE("write(\"" << lovecraft << "\")"),
+ strq.write(&lovecraft[0], lovecraft.length()), lovecraft.length());
+ size_t peeklen(strq.peek(&buffer[0], buffer.size()));
+ ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\")"),
+ peeklen, lovecraft.length());
+ ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\") result"),
+ std::string(buffer.begin(), buffer.begin() + peeklen), lovecraft);
+ std::streamsize skip1(4);
+ ensure_equals(STRINGIZE("skip(" << skip1 << ")"), strq.skip(skip1), skip1);
+ ensure_equals("size() after skip()", strq.size(), lovecraft.length() - skip1);
+ size_t readlen(strq.read(&buffer[0], buffer.size()));
+ ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\")"),
+ readlen, lovecraft.length() - skip1);
+ ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\") result"),
+ std::string(buffer.begin(), buffer.begin() + readlen),
+ lovecraft.substr(skip1));
+ ensure_equals("unconsumed", strq.read(&buffer[0], buffer.size()), 0);
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("skip() multiple blocks");
+ std::string blocks[] = { "books of ", "H.P. ", "Lovecraft" };
+ std::streamsize total(blocks[0].length() + blocks[1].length() + blocks[2].length());
+ std::streamsize leave(5); // len("craft") above
+ std::streamsize skip(total - leave);
+ std::streamsize written(0);
+ BOOST_FOREACH(const std::string& block, blocks)
+ {
+ written += strq.write(&block[0], block.length());
+ ensure_equals("size() after write()", strq.size(), written);
+ }
+ std::streamsize skiplen(strq.skip(skip));
+ ensure_equals(STRINGIZE("skip(" << skip << ")"), skiplen, skip);
+ ensure_equals("size() after skip()", strq.size(), leave);
+ size_t readlen(strq.read(&buffer[0], buffer.size()));
+ ensure_equals("read(\"craft\")", readlen, leave);
+ ensure_equals("read(\"craft\") result",
+ std::string(buffer.begin(), buffer.begin() + readlen), "craft");
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("concatenate blocks");
+ std::string blocks[] = { "abcd", "efghij", "klmnopqrs" };
+ BOOST_FOREACH(const std::string& block, blocks)
+ {
+ strq.write(&block[0], block.length());
+ }
+ std::vector<char> longbuffer(30);
+ std::streamsize readlen(strq.read(&longbuffer[0], longbuffer.size()));
+ ensure_equals("read() multiple blocks",
+ readlen, blocks[0].length() + blocks[1].length() + blocks[2].length());
+ ensure_equals("read() multiple blocks result",
+ std::string(longbuffer.begin(), longbuffer.begin() + readlen),
+ blocks[0] + blocks[1] + blocks[2]);
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("split blocks");
+ std::string blocks[] = { "abcdefghijklm", "nopqrstuvwxyz" };
+ BOOST_FOREACH(const std::string& block, blocks)
+ {
+ strq.write(&block[0], block.length());
+ }
+ strq.close();
+ // We've already verified what strq.size() should be at this point;
+ // see above test named "skip() multiple blocks"
+ std::streamsize chksize(strq.size());
+ std::streamsize readlen(strq.read(&buffer[0], buffer.size()));
+ ensure_equals("read() 0", readlen, buffer.size());
+ ensure_equals("read() 0 result", std::string(buffer.begin(), buffer.end()), "abcdefghij");
+ chksize -= readlen;
+ ensure_equals("size() after read() 0", strq.size(), chksize);
+ readlen = strq.read(&buffer[0], buffer.size());
+ ensure_equals("read() 1", readlen, buffer.size());
+ ensure_equals("read() 1 result", std::string(buffer.begin(), buffer.end()), "klmnopqrst");
+ chksize -= readlen;
+ ensure_equals("size() after read() 1", strq.size(), chksize);
+ readlen = strq.read(&buffer[0], buffer.size());
+ ensure_equals("read() 2", readlen, chksize);
+ ensure_equals("read() 2 result",
+ std::string(buffer.begin(), buffer.begin() + readlen), "uvwxyz");
+ ensure_equals("read() 3", strq.read(&buffer[0], buffer.size()), -1);
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index beba55416a..a7aa347222 100644..100755
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -4,38 +4,36 @@
* @date 2006-12-24
* @brief Test cases of llstring.cpp
*
- * $LicenseInfo:firstyear=2007&license=viewergpl$
- *
- * Copyright (c) 2007-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
-#include "../test/lltut.h"
+#include <boost/assign/list_of.hpp>
#include "../llstring.h"
+#include "StringVec.h" // must come BEFORE lltut.h
+#include "../test/lltut.h"
+
+using boost::assign::list_of;
namespace tut
{
@@ -44,7 +42,7 @@ namespace tut
};
typedef test_group<string_index> string_index_t;
typedef string_index_t::object string_index_object_t;
- tut::string_index_t tut_string_index("string_test");
+ tut::string_index_t tut_string_index("LLString");
template<> template<>
void string_index_object_t::test<1>()
@@ -630,6 +628,14 @@ namespace tut
subcount = LLStringUtil::format(s, fmt_map);
ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]");
ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
+ // Test on nested brackets
+ std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]");
+ ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
// Test an assorted substitution
std::string srcs8 = "foo[DELETE]bar?";
@@ -748,4 +754,118 @@ namespace tut
ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
}
+
+ template<> template<>
+ void string_index_object_t::test<41>()
+ {
+ set_test_name("getTokens(\"delims\")");
+ ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec());
+ ensure_equals("only delims",
+ LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec());
+ ensure_equals("sequence of delims",
+ LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
+ // nat considers this a dubious implementation side effect, but I'd
+ // hate to change it now...
+ ensure_equals("noncontiguous tokens",
+ LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
+ ensure_equals("space-padded tokens",
+ LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two"));
+ ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
+ }
+
+ // Shorthand for verifying that getTokens() behaves the same when you
+ // don't pass a string of escape characters, when you pass an empty string
+ // (different overloads), and when you pass a string of characters that
+ // aren't actually present.
+ void ensure_getTokens(const std::string& desc,
+ const std::string& string,
+ const std::string& drop_delims,
+ const std::string& keep_delims,
+ const std::string& quotes,
+ const std::vector<std::string>& expect)
+ {
+ ensure_equals(desc + " - no esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes),
+ expect);
+ ensure_equals(desc + " - empty esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""),
+ expect);
+ ensure_equals(desc + " - unused esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"),
+ expect);
+ }
+
+ void ensure_getTokens(const std::string& desc,
+ const std::string& string,
+ const std::string& drop_delims,
+ const std::string& keep_delims,
+ const std::vector<std::string>& expect)
+ {
+ ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<42>()
+ {
+ set_test_name("getTokens(\"delims\", etc.)");
+ // Signatures to test in this method:
+ // getTokens(string, drop_delims, keep_delims [, quotes [, escapes]])
+ // If you omit keep_delims, you get the older function (test above).
+
+ // cases like the getTokens(string, delims) tests above
+ ensure_getTokens("empty string", "", " ", "", StringVec());
+ ensure_getTokens("only delims",
+ " \r\n ", " \r\n", "", StringVec());
+ ensure_getTokens("sequence of delims",
+ ",,, one ,,,", ", ", "", list_of("one"));
+ // Note contrast with the case in the previous method
+ ensure_getTokens("noncontiguous tokens",
+ ", ,, , one ,,,", ", ", "", list_of("one"));
+ ensure_getTokens("space-padded tokens",
+ ", one , two ,", ", ", "",
+ list_of("one")("two"));
+ ensure_getTokens("no delims", "one", ",", "", list_of("one"));
+
+ // drop_delims vs. keep_delims
+ ensure_getTokens("arithmetic",
+ " ab+def / xx* yy ", " ", "+-*/",
+ list_of("ab")("+")("def")("/")("xx")("*")("yy"));
+
+ // quotes
+ ensure_getTokens("no quotes",
+ "She said, \"Don't go.\"", " ", ",", "",
+ list_of("She")("said")(",")("\"Don't")("go.\""));
+ ensure_getTokens("quotes",
+ "She said, \"Don't go.\"", " ", ",", "\"",
+ list_of("She")("said")(",")("Don't go."));
+ ensure_getTokens("quotes and delims",
+ "run c:/'Documents and Settings'/someone", " ", "", "'",
+ list_of("run")("c:/Documents and Settings/someone"));
+ ensure_getTokens("unmatched quote",
+ "baby don't leave", " ", "", "'",
+ list_of("baby")("don't")("leave"));
+ ensure_getTokens("adjacent quoted",
+ "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
+ list_of("abcdef \"ghijkl' mnopqr"));
+ ensure_getTokens("quoted empty string",
+ "--set SomeVar ''", " ", "", "'",
+ list_of("--set")("SomeVar")(""));
+
+ // escapes
+ // Don't use backslash as an escape for these tests -- you'll go nuts
+ // between the C++ string scanner and getTokens() escapes. Test with
+ // something else!
+ ensure_equals("escaped delims",
+ LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
+ list_of(" a")("-")("dog-gone phrase"));
+ ensure_equals("escaped quotes",
+ LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
+ list_of("say:")("this isn't working."));
+ ensure_equals("escaped escape",
+ LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
+ list_of("want")("x^2"));
+ ensure_equals("escape at end",
+ LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
+ list_of("it's up")("there^"));
+ }
}
diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp
new file mode 100644
index 0000000000..0a9d85ad00
--- /dev/null
+++ b/indra/llcommon/tests/lltrace_test.cpp
@@ -0,0 +1,142 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
+#include "lltracerecording.h"
+#include "../test/lltut.h"
+
+namespace LLUnits
+{
+ // using powers of 2 to allow strict floating point equality
+ LL_DECLARE_BASE_UNIT(Ounces, "oz");
+ LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12);
+ LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16);
+ LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20);
+
+ LL_DECLARE_BASE_UNIT(Grams, "g");
+ LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ounces);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, TallCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, GrandeCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, VentiCup);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Grams);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams);
+
+
+namespace tut
+{
+ using namespace LLTrace;
+ struct trace
+ {
+ ThreadRecorder mRecorder;
+ };
+
+ typedef test_group<trace> trace_t;
+ typedef trace_t::object trace_object_t;
+ tut::trace_t tut_singleton("LLTrace");
+
+ static CountStatHandle<S32> sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast.");
+ static SampleStatHandle<F32Milligrams> sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient");
+ static EventStatHandle<S32Ounces> sOuncesPerCup("cupsize", "Large, huge, or ginormous");
+
+ static F32 sCaffeineLevel(0.f);
+ const F32Milligrams sCaffeinePerOz(18.f);
+
+ void drink_coffee(S32 num_cups, S32Ounces cup_size)
+ {
+ add(sCupsOfCoffeeConsumed, num_cups);
+ for (S32 i = 0; i < num_cups; i++)
+ {
+ record(sOuncesPerCup, cup_size);
+ }
+
+ sCaffeineLevel += F32Ounces(num_cups * cup_size).value() * sCaffeinePerOz.value();
+ sample(sCaffeineLevelStat, sCaffeineLevel);
+ }
+
+ // basic data collection
+ template<> template<>
+ void trace_object_t::test<1>()
+ {
+ sample(sCaffeineLevelStat, sCaffeineLevel);
+
+ Recording all_day;
+ Recording at_work;
+ Recording after_3pm;
+
+ all_day.start();
+ {
+ // warm up with one grande cup
+ drink_coffee(1, S32TallCup(1));
+
+ // go to work
+ at_work.start();
+ {
+ // drink 3 tall cups, 1 after 3 pm
+ drink_coffee(2, S32GrandeCup(1));
+ after_3pm.start();
+ drink_coffee(1, S32GrandeCup(1));
+ }
+ at_work.stop();
+ drink_coffee(1, S32VentiCup(1));
+ }
+ // don't need to stop recordings to get accurate values out of them
+ //after_3pm.stop();
+ //all_day.stop();
+
+ ensure("count stats are counted when recording is active",
+ at_work.getSum(sCupsOfCoffeeConsumed) == 3
+ && all_day.getSum(sCupsOfCoffeeConsumed) == 5
+ && after_3pm.getSum(sCupsOfCoffeeConsumed) == 2);
+ ensure("measurement sums are counted when recording is active",
+ at_work.getSum(sOuncesPerCup) == S32Ounces(48)
+ && all_day.getSum(sOuncesPerCup) == S32Ounces(80)
+ && after_3pm.getSum(sOuncesPerCup) == S32Ounces(36));
+ ensure("measurement min is specific to when recording is active",
+ at_work.getMin(sOuncesPerCup) == S32GrandeCup(1)
+ && all_day.getMin(sOuncesPerCup) == S32TallCup(1)
+ && after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1));
+ ensure("measurement max is specific to when recording is active",
+ at_work.getMax(sOuncesPerCup) == S32GrandeCup(1)
+ && all_day.getMax(sOuncesPerCup) == S32VentiCup(1)
+ && after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1));
+ ensure("sample min is specific to when recording is active",
+ at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value()
+ && all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f)
+ && after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value());
+ ensure("sample max is specific to when recording is active",
+ at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value()
+ && all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()
+ && after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value());
+ }
+
+}
diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp
index 31c70b4daa..1d619867d4 100644..100755
--- a/indra/llcommon/tests/lltreeiterators_test.cpp
+++ b/indra/llcommon/tests/lltreeiterators_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-08-20
* @brief Test of lltreeiterators.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -62,7 +56,7 @@ namespace tut
};
typedef test_group<iter_data> iter_group;
typedef iter_group::object iter_object;
- tut::iter_group ig("lltreeiterators");
+ tut::iter_group ig("LLTreeIterators");
} // namespace tut
/*****************************************************************************
diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp
new file mode 100644
index 0000000000..57cf9810af
--- /dev/null
+++ b/indra/llcommon/tests/llunits_test.cpp
@@ -0,0 +1,388 @@
+/**
+ * @file llsingleton_test.cpp
+ * @date 2011-08-11
+ * @brief Unit test for the LLSingleton class
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llunits.h"
+#include "../test/lltut.h"
+
+namespace LLUnits
+{
+ // using powers of 2 to allow strict floating point equality
+ LL_DECLARE_BASE_UNIT(Quatloos, "Quat");
+ LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4);
+ LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Latinum);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari);
+
+namespace LLUnits
+{
+ LL_DECLARE_BASE_UNIT(Celcius, "c");
+ LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32);
+ LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f);
+}
+
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Celcius);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Fahrenheit);
+LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kelvin);
+
+
+namespace tut
+{
+ using namespace LLUnits;
+ struct units
+ {
+ };
+
+ typedef test_group<units> units_t;
+ typedef units_t::object units_object_t;
+ tut::units_t tut_singleton("LLUnit");
+
+ // storage type conversions
+ template<> template<>
+ void units_object_t::test<1>()
+ {
+ LLUnit<F32, Quatloos> float_quatloos;
+ ensure("default float unit is zero", float_quatloos == F32Quatloos(0.f));
+
+ LLUnit<F32, Quatloos> float_initialize_quatloos(1);
+ ensure("non-zero initialized unit", float_initialize_quatloos == F32Quatloos(1.f));
+
+ LLUnit<S32, Quatloos> int_quatloos;
+ ensure("default int unit is zero", int_quatloos == S32Quatloos(0));
+
+ int_quatloos = S32Quatloos(42);
+ ensure("int assignment is preserved", int_quatloos == S32Quatloos(42));
+ float_quatloos = int_quatloos;
+ ensure("float assignment from int preserves value", float_quatloos == F32Quatloos(42.f));
+
+ int_quatloos = float_quatloos;
+ ensure("int assignment from float preserves value", int_quatloos == S32Quatloos(42));
+
+ float_quatloos = F32Quatloos(42.1f);
+ int_quatloos = float_quatloos;
+ ensure("int units truncate float units on assignment", int_quatloos == S32Quatloos(42));
+
+ LLUnit<U32, Quatloos> unsigned_int_quatloos(float_quatloos);
+ ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42));
+
+ S32Solari int_solari(1);
+
+ float_quatloos = int_solari;
+ ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f));
+
+ int_quatloos = S32Quatloos(1);
+ F32Solari float_solari = int_quatloos;
+ ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f));
+ }
+
+ // conversions to/from base unit
+ template<> template<>
+ void units_object_t::test<2>()
+ {
+ LLUnit<F32, Quatloos> quatloos(1.f);
+ LLUnit<F32, Latinum> latinum_bars(quatloos);
+ ensure("conversion between units is automatic via initialization", latinum_bars == F32Latinum(1.f / 4.f));
+
+ latinum_bars = S32Latinum(256);
+ quatloos = latinum_bars;
+ ensure("conversion between units is automatic via assignment, and bidirectional", quatloos == S32Quatloos(1024));
+
+ LLUnit<S32, Quatloos> single_quatloo(1);
+ LLUnit<F32, Latinum> quarter_latinum = single_quatloo;
+ ensure("division of integer unit preserves fractional values when converted to float unit", quarter_latinum == F32Latinum(0.25f));
+ }
+
+ // conversions across non-base units
+ template<> template<>
+ void units_object_t::test<3>()
+ {
+ LLUnit<F32, Quatloos> quatloos(1024);
+ LLUnit<F32, Solari> solari(quatloos);
+ ensure("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari", solari == S32Solari(4096));
+
+ LLUnit<F32, Latinum> latinum_bars = solari;
+ ensure("Non base units can be converted between each other", latinum_bars == S32Latinum(256));
+ }
+
+ // math operations
+ template<> template<>
+ void units_object_t::test<4>()
+ {
+ // exercise math operations
+ LLUnit<F32, Quatloos> quatloos(1.f);
+ quatloos *= 4.f;
+ ensure(quatloos == S32Quatloos(4));
+ quatloos = quatloos * 2;
+ ensure(quatloos == S32Quatloos(8));
+ quatloos = 2.f * quatloos;
+ ensure(quatloos == S32Quatloos(16));
+
+ quatloos += F32Quatloos(4.f);
+ ensure(quatloos == S32Quatloos(20));
+ quatloos += S32Quatloos(4);
+ ensure(quatloos == S32Quatloos(24));
+ quatloos = quatloos + S32Quatloos(4);
+ ensure(quatloos == S32Quatloos(28));
+ quatloos = S32Quatloos(4) + quatloos;
+ ensure(quatloos == S32Quatloos(32));
+ quatloos += quatloos * 3;
+ ensure(quatloos == S32Quatloos(128));
+
+ quatloos -= quatloos / 4 * 3;
+ ensure(quatloos == S32Quatloos(32));
+ quatloos = quatloos - S32Quatloos(8);
+ ensure(quatloos == S32Quatloos(24));
+ quatloos -= S32Quatloos(4);
+ ensure(quatloos == S32Quatloos(20));
+ quatloos -= F32Quatloos(4.f);
+ ensure(quatloos == S32Quatloos(16));
+
+ quatloos /= 2.f;
+ ensure(quatloos == S32Quatloos(8));
+ quatloos = quatloos / 4;
+ ensure(quatloos == S32Quatloos(2));
+
+ F32 ratio = quatloos / LLUnit<F32, Quatloos>(2.f);
+ ensure(ratio == 1);
+ ratio = quatloos / LLUnit<F32, Solari>(8.f);
+ ensure(ratio == 1);
+
+ quatloos += LLUnit<F32, Solari>(8.f);
+ ensure(quatloos == S32Quatloos(4));
+ quatloos -= LLUnit<F32, Latinum>(1.f);
+ ensure(quatloos == S32Quatloos(0));
+ }
+
+ // comparison operators
+ template<> template<>
+ void units_object_t::test<5>()
+ {
+ LLUnit<S32, Quatloos> quatloos(1);
+ ensure("can perform less than comparison against same type", quatloos < S32Quatloos(2));
+ ensure("can perform less than comparison against different storage type", quatloos < F32Quatloos(2.f));
+ ensure("can perform less than comparison against different units", quatloos < S32Latinum(5));
+ ensure("can perform less than comparison against different storage type and units", quatloos < F32Latinum(5.f));
+
+ ensure("can perform greater than comparison against same type", quatloos > S32Quatloos(0));
+ ensure("can perform greater than comparison against different storage type", quatloos > F32Quatloos(0.f));
+ ensure("can perform greater than comparison against different units", quatloos > S32Latinum(0));
+ ensure("can perform greater than comparison against different storage type and units", quatloos > F32Latinum(0.f));
+
+ }
+
+ bool accept_explicit_quatloos(S32Quatloos q)
+ {
+ return true;
+ }
+
+ bool accept_implicit_quatloos(S32Quatloos q)
+ {
+ return true;
+ }
+
+ // signature compatibility
+ template<> template<>
+ void units_object_t::test<6>()
+ {
+ S32Quatloos quatloos(1);
+ ensure("can pass unit values as argument", accept_explicit_quatloos(S32Quatloos(1)));
+ ensure("can pass unit values as argument", accept_explicit_quatloos(quatloos));
+ }
+
+ // implicit units
+ template<> template<>
+ void units_object_t::test<7>()
+ {
+ LLUnit<F32, Quatloos> quatloos;
+ LLUnitImplicit<F32, Quatloos> quatloos_implicit = quatloos + S32Quatloos(1);
+ ensure("can initialize implicit unit from explicit", quatloos_implicit == 1);
+
+ quatloos = quatloos_implicit;
+ ensure("can assign implicit unit to explicit unit", quatloos == S32Quatloos(1));
+ quatloos += quatloos_implicit;
+ ensure("can perform math operation using mixture of implicit and explicit units", quatloos == S32Quatloos(2));
+
+ // math operations on implicits
+ quatloos_implicit = 1;
+ ensure(quatloos_implicit == 1);
+
+ quatloos_implicit += 2;
+ ensure(quatloos_implicit == 3);
+
+ quatloos_implicit *= 2;
+ ensure(quatloos_implicit == 6);
+
+ quatloos_implicit -= 1;
+ ensure(quatloos_implicit == 5);
+
+ quatloos_implicit /= 5;
+ ensure(quatloos_implicit == 1);
+
+ quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit;
+ ensure(quatloos_implicit == 5);
+
+ quatloos_implicit = 10 - quatloos_implicit - 1;
+ ensure(quatloos_implicit == 4);
+
+ quatloos_implicit = 2 * quatloos_implicit * 2;
+ ensure(quatloos_implicit == 16);
+
+ F32 one_half = quatloos_implicit / (quatloos_implicit * 2);
+ ensure(one_half == 0.5f);
+
+ // implicit conversion to POD
+ F32 float_val = quatloos_implicit;
+ ensure("implicit units convert implicitly to regular values", float_val == 16);
+
+ S32 int_val = quatloos_implicit;
+ ensure("implicit units convert implicitly to regular values", int_val == 16);
+
+ // conversion of implicits
+ LLUnitImplicit<F32, Latinum> latinum_implicit(2);
+ ensure("implicit units of different types are comparable", latinum_implicit * 2 == quatloos_implicit);
+
+ quatloos_implicit += F32Quatloos(10);
+ ensure("can add-assign explicit units", quatloos_implicit == 26);
+
+ quatloos_implicit -= F32Quatloos(10);
+ ensure("can subtract-assign explicit units", quatloos_implicit == 16);
+
+ // comparisons
+ ensure("can compare greater than implicit unit", quatloos_implicit > F32QuatloosImplicit(0.f));
+ ensure("can compare greater than non-implicit unit", quatloos_implicit > F32Quatloos(0.f));
+ ensure("can compare greater than or equal to implicit unit", quatloos_implicit >= F32QuatloosImplicit(0.f));
+ ensure("can compare greater than or equal to non-implicit unit", quatloos_implicit >= F32Quatloos(0.f));
+ ensure("can compare less than implicit unit", quatloos_implicit < F32QuatloosImplicit(20.f));
+ ensure("can compare less than non-implicit unit", quatloos_implicit < F32Quatloos(20.f));
+ ensure("can compare less than or equal to implicit unit", quatloos_implicit <= F32QuatloosImplicit(20.f));
+ ensure("can compare less than or equal to non-implicit unit", quatloos_implicit <= F32Quatloos(20.f));
+ }
+
+ // precision tests
+ template<> template<>
+ void units_object_t::test<8>()
+ {
+ U32Bytes max_bytes(U32_MAX);
+ S32Megabytes mega_bytes = max_bytes;
+ ensure("max available precision is used when converting units", mega_bytes == (S32Megabytes)4095);
+
+ mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1;
+ ensure("can mix signed and unsigned in units addition", mega_bytes == (S32Megabytes)-4);
+
+ mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1;
+ ensure("can mix unsigned and signed in units addition", mega_bytes == (S32Megabytes)4);
+ }
+
+ // default units
+ template<> template<>
+ void units_object_t::test<9>()
+ {
+ U32Gigabytes GB(1);
+ U32Megabytes MB(GB);
+ U32Kilobytes KB(GB);
+ U32Bytes B(GB);
+
+ ensure("GB -> MB conversion", MB.value() == 1024);
+ ensure("GB -> KB conversion", KB.value() == 1024 * 1024);
+ ensure("GB -> B conversion", B.value() == 1024 * 1024 * 1024);
+
+ KB = U32Kilobytes(1);
+ U32Kilobits Kb(KB);
+ U32Bits b(KB);
+ ensure("KB -> Kb conversion", Kb.value() == 8);
+ ensure("KB -> b conversion", b.value() == 8 * 1024);
+
+ U32Days days(1);
+ U32Hours hours(days);
+ U32Minutes minutes(days);
+ U32Seconds seconds(days);
+ U32Milliseconds ms(days);
+
+ ensure("days -> hours conversion", hours.value() == 24);
+ ensure("days -> minutes conversion", minutes.value() == 24 * 60);
+ ensure("days -> seconds conversion", seconds.value() == 24 * 60 * 60);
+ ensure("days -> ms conversion", ms.value() == 24 * 60 * 60 * 1000);
+
+ U32Kilometers km(1);
+ U32Meters m(km);
+ U32Centimeters cm(km);
+ U32Millimeters mm(km);
+
+ ensure("km -> m conversion", m.value() == 1000);
+ ensure("km -> cm conversion", cm.value() == 1000 * 100);
+ ensure("km -> mm conversion", mm.value() == 1000 * 1000);
+
+ U32Gigahertz GHz(1);
+ U32Megahertz MHz(GHz);
+ U32Kilohertz KHz(GHz);
+ U32Hertz Hz(GHz);
+
+ ensure("GHz -> MHz conversion", MHz.value() == 1000);
+ ensure("GHz -> KHz conversion", KHz.value() == 1000 * 1000);
+ ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000);
+
+ F32Radians rad(6.2831853071795f);
+ S32Degrees deg(rad);
+ ensure("radians -> degrees conversion", deg.value() == 360);
+
+ F32Percent percent(50);
+ F32Ratio ratio(percent);
+ ensure("percent -> ratio conversion", ratio.value() == 0.5f);
+
+ U32Kilotriangles ktris(1);
+ U32Triangles tris(ktris);
+ ensure("kilotriangles -> triangles conversion", tris.value() == 1000);
+ }
+
+ bool value_near(F32 value, F32 target, F32 threshold)
+ {
+ return fabsf(value - target) < threshold;
+ }
+
+ // linear transforms
+ template<> template<>
+ void units_object_t::test<10>()
+ {
+ F32Celcius float_celcius(100);
+ F32Fahrenheit float_fahrenheit(float_celcius);
+ ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) );
+
+ float_celcius = float_fahrenheit;
+ ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) );
+
+ S32Celcius int_celcius(100);
+ S32Fahrenheit int_fahrenheit(int_celcius);
+ ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212);
+
+ int_celcius = int_fahrenheit;
+ ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100);
+ }
+}
diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp
index 0a7c37d4b9..4c64f15ca7 100644..100755
--- a/indra/llcommon/tests/lluri_test.cpp
+++ b/indra/llcommon/tests/lluri_test.cpp
@@ -3,31 +3,25 @@
* @brief LLURI unit tests
* @date September 2006
*
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -64,12 +58,12 @@ namespace tut
ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1);
}
};
-
+
typedef test_group<URITestData> URITestGroup;
typedef URITestGroup::object URITestObject;
URITestGroup uriTestGroup("LLURI");
-
+
template<> template<>
void URITestObject::test<1>()
{
@@ -95,14 +89,14 @@ namespace tut
template<> template<>
void URITestObject::test<2>()
{
- // empty string
+ set_test_name("empty string");
checkParts(LLURI(""), "", "", "", "");
}
-
+
template<> template<>
void URITestObject::test<3>()
{
- // no scheme
+ set_test_name("no scheme");
checkParts(LLURI("foo"), "", "foo", "", "");
checkParts(LLURI("foo%3A"), "", "foo:", "", "");
}
@@ -110,7 +104,7 @@ namespace tut
template<> template<>
void URITestObject::test<4>()
{
- // scheme w/o paths
+ set_test_name("scheme w/o paths");
checkParts(LLURI("mailto:zero@ll.com"),
"mailto", "zero@ll.com", "", "");
checkParts(LLURI("silly://abc/def?foo"),
@@ -120,16 +114,16 @@ namespace tut
template<> template<>
void URITestObject::test<5>()
{
- // authority section
+ set_test_name("authority section");
checkParts(LLURI("http:///"),
"http", "///", "", "/");
-
+
checkParts(LLURI("http://abc"),
"http", "//abc", "abc", "");
-
+
checkParts(LLURI("http://a%2Fb/cd"),
"http", "//a/b/cd", "a/b", "/cd");
-
+
checkParts(LLURI("http://host?"),
"http", "//host?", "host", "");
}
@@ -137,13 +131,13 @@ namespace tut
template<> template<>
void URITestObject::test<6>()
{
- // path section
+ set_test_name("path section");
checkParts(LLURI("http://host/a/b/"),
"http", "//host/a/b/", "host", "/a/b/");
-
+
checkParts(LLURI("http://host/a%3Fb/"),
"http", "//host/a?b/", "host", "/a?b/");
-
+
checkParts(LLURI("http://host/a:b/"),
"http", "//host/a:b/", "host", "/a:b/");
}
@@ -151,16 +145,16 @@ namespace tut
template<> template<>
void URITestObject::test<7>()
{
- // query string
+ set_test_name("query string");
checkParts(LLURI("http://host/?"),
"http", "//host/?", "host", "/", "");
-
+
checkParts(LLURI("http://host/?x"),
"http", "//host/?x", "host", "/", "x");
-
+
checkParts(LLURI("http://host/??"),
"http", "//host/??", "host", "/", "?");
-
+
checkParts(LLURI("http://host/?%3F"),
"http", "//host/??", "host", "/", "?");
}
@@ -173,19 +167,44 @@ namespace tut
path.append("123");
checkParts(LLURI::buildHTTP("host", path),
"http", "//host/x/123", "host", "/x/123");
-
+
LLSD query;
query["123"] = "12";
query["abcd"] = "abc";
checkParts(LLURI::buildHTTP("host", path, query),
"http", "//host/x/123?123=12&abcd=abc",
"host", "/x/123", "123=12&abcd=abc");
+
+ ensure_equals(LLURI::buildHTTP("host", "").asString(),
+ "http://host");
+ ensure_equals(LLURI::buildHTTP("host", "/").asString(),
+ "http://host/");
+ ensure_equals(LLURI::buildHTTP("host", "//").asString(),
+ "http://host/");
+ ensure_equals(LLURI::buildHTTP("host", "dir name").asString(),
+ "http://host/dir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(),
+ "http://host/dir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(),
+ "http://host/dir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(),
+ "http://host/dir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(),
+ "http://host/dir%20name/subdir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(),
+ "http://host/dir%20name/subdir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(),
+ "http://host/dir%20name/subdir%20name");
+ ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(),
+ "http://host/dir%20name/subdir%20name/");
+ ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(),
+ "http://host/dir%20name/subdir%20name/");
}
template<> template<>
void URITestObject::test<9>()
{
- // test unescaped path components
+ set_test_name("test unescaped path components");
LLSD path;
path.append("x@*//*$&^");
path.append("123");
@@ -196,7 +215,7 @@ namespace tut
template<> template<>
void URITestObject::test<10>()
{
- // test unescaped query components
+ set_test_name("test unescaped query components");
LLSD path;
path.append("x");
path.append("123");
@@ -211,7 +230,7 @@ namespace tut
template<> template<>
void URITestObject::test<11>()
{
- // test unescaped host components
+ set_test_name("test unescaped host components");
LLSD path;
path.append("x");
path.append("123");
@@ -222,16 +241,16 @@ namespace tut
"http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc",
"hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc");
}
-
+
template<> template<>
void URITestObject::test<12>()
{
- // test funky host_port values that are actually prefixes
-
+ set_test_name("test funky host_port values that are actually prefixes");
+
checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()),
"http", "//example.com:8080",
"example.com:8080", "");
-
+
checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()),
"http", "//example.com:8080/",
"example.com:8080", "/");
@@ -248,7 +267,7 @@ namespace tut
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789"
"-._~";
- // test escape
+ set_test_name("test escape");
ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67");
ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40");
ensure_equals("escaping as query variable",
@@ -265,13 +284,12 @@ namespace tut
cedilla.push_back( (char)0xA7 );
ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7");
}
-
+
template<> template<>
void URITestObject::test<14>()
{
- // make sure escape and unescape of empty strings return empty
- // strings.
+ set_test_name("make sure escape and unescape of empty strings return empty strings.");
std::string uri_esc(LLURI::escape(""));
ensure("escape string empty", uri_esc.empty());
std::string uri_raw(LLURI::unescape(""));
@@ -281,7 +299,7 @@ namespace tut
template<> template<>
void URITestObject::test<15>()
{
- // do some round-trip tests
+ set_test_name("do some round-trip tests");
escapeRoundTrip("http://secondlife.com");
escapeRoundTrip("http://secondlife.com/url with spaces");
escapeRoundTrip("http://bad[domain]name.com/");
@@ -292,7 +310,7 @@ namespace tut
template<> template<>
void URITestObject::test<16>()
{
- // Test the default escaping
+ set_test_name("Test the default escaping");
// yes -- this mangles the url. This is expected behavior
std::string simple("http://secondlife.com");
ensure_equals(
@@ -308,7 +326,7 @@ namespace tut
template<> template<>
void URITestObject::test<17>()
{
- // do some round-trip tests with very long strings.
+ set_test_name("do some round-trip tests with very long strings.");
escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six");
escapeRoundTrip(
"'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale"
@@ -328,7 +346,7 @@ namespace tut
"D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n");
}
-
+
template<> template<>
void URITestObject::test<18>()
{
@@ -341,7 +359,7 @@ namespace tut
ensure_equals("pathmap", u.pathArray()[1].asString(), "login");
ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test");
ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester");
-
+
u = LLURI("secondlife://Da Boom/128/128/128");
// if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority
ensure_equals("scheme", u.scheme(), "secondlife");
@@ -356,7 +374,7 @@ namespace tut
template<> template<>
void URITestObject::test<19>()
{
- // Parse about: schemes
+ set_test_name("Parse about: schemes");
LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78");
ensure_equals("scheme", u.scheme(), "about");
ensure_equals("authority", u.authority(), "");
diff --git a/indra/llcommon/tests/reflection_test.cpp b/indra/llcommon/tests/reflection_test.cpp
deleted file mode 100644
index 5263e7fa64..0000000000
--- a/indra/llcommon/tests/reflection_test.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-/**
- * @file reflection_test.cpp
- * @date May 2006
- * @brief Reflection unit tests.
- *
- * $LicenseInfo:firstyear=2006&license=viewergpl$
- *
- * Copyright (c) 2006-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "../linden_common.h"
-#include "../reflective.h"
-#include "../metaclasst.h"
-#include "../metapropertyt.h"
-#include "../stdtypes.h"
-
-#include "../test/lltut.h"
-
-namespace tut
-{
- class TestAggregatedData : public LLReflective
- {
- public:
- TestAggregatedData() {;}
- virtual const LLMetaClass& getMetaClass() const;
-
- private:
- };
-
- class TestReflectionData : public LLReflective
- {
- public:
- TestReflectionData() : mInt(42), mString("foo"), mNullPtr(NULL), mPtr(new TestAggregatedData()), mRef(*(new TestAggregatedData)) {;}
- virtual ~TestReflectionData() {delete mPtr;}
- virtual const LLMetaClass& getMetaClass() const;
-
- static U32 getPropertyCount() {return 5;}
-
- private:
-
- friend class LLMetaClassT<TestReflectionData>;
- S32 mInt;
- std::string mString;
- TestAggregatedData* mNullPtr;
- TestAggregatedData* mPtr;
- TestAggregatedData mObj;
- TestAggregatedData& mRef;
- };
-}
-
-template <>
-void LLMetaClassT<tut::TestReflectionData>::reflectProperties(LLMetaClass& meta_class)
-{
- reflectProperty(meta_class, "mInt", &tut::TestReflectionData::mInt);
- reflectProperty(meta_class, "mString", &tut::TestReflectionData::mString);
- reflectPtrProperty(meta_class, "mNullPtr", &tut::TestReflectionData::mNullPtr);
- reflectPtrProperty(meta_class, "mPtr", &tut::TestReflectionData::mPtr);
- reflectProperty(meta_class, "mObj", &tut::TestReflectionData::mObj);
- //reflectProperty(meta_class, "mRef", &tut::TestReflectionData::mRef); // AARGH!
-}
-
-namespace tut
-{
- // virtual
- const LLMetaClass& TestReflectionData::getMetaClass() const
- {
- return LLMetaClassT<TestReflectionData>::instance();
- }
-
- const LLMetaClass& TestAggregatedData::getMetaClass() const
- {
- return LLMetaClassT<TestAggregatedData>::instance();
- }
-}
-
-namespace tut
-{
- typedef tut::test_group<TestReflectionData> TestReflectionGroup;
- typedef TestReflectionGroup::object TestReflectionObject;
- TestReflectionGroup gTestReflectionGroup("reflection");
-
- template<> template<>
- void TestReflectionObject::test<1>()
- {
- // Check properties can be found.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLMetaProperty* null = NULL;
- ensure_not_equals(meta_class.findProperty("mInt"), null);
- ensure_not_equals(meta_class.findProperty("mString"), null);
- }
-
- template<> template<>
- void TestReflectionObject::test<2>()
- {
- // Check non-existent property cannot be found.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLMetaProperty* null = NULL;
- ensure_equals(meta_class.findProperty("foo"), null);
- }
-
- template<> template<>
- void TestReflectionObject::test<3>()
- {
- // Check integer property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- ensure_equals(meta_class.findProperty("mInt")->getLLSD(this).asInteger(), 42);
- }
-
- template<> template<>
- void TestReflectionObject::test<4>()
- {
- // Check string property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- ensure_equals(meta_class.findProperty("mString")->getLLSD(this).asString(), std::string("foo"));
- }
-
- template<> template<>
- void TestReflectionObject::test<5>()
- {
- // Check NULL reference property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLReflective* null = NULL;
- ensure_equals(meta_class.findProperty("mNullPtr")->get(this), null);
- }
-
- template<> template<>
- void TestReflectionObject::test<6>()
- {
- // Check reference property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLReflective* null = NULL;
- const LLReflective* ref = meta_class.findProperty("mPtr")->get(this);
- ensure_not_equals(ref, null);
- }
-
- template<> template<>
- void TestReflectionObject::test<7>()
- {
- // Check reflective property has correct value.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- const LLReflective* null = NULL;
- const LLReflective* ref = meta_class.findProperty("mObj")->get(this);
- ensure_not_equals(ref, null);
- }
-
- template<> template<>
- void TestReflectionObject::test<8>()
- {
- // Check property count.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- ensure_equals(meta_class.getPropertyCount(), TestReflectionData::getPropertyCount());
- }
-
- template<> template<>
- void TestReflectionObject::test<9>()
- {
- // Check property iteration.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- U32 count = 0;
- LLMetaClass::PropertyIterator iter;
- for(iter = meta_class.beginProperties(); iter != meta_class.endProperties(); ++iter)
- {
- ++count;
- }
- ensure_equals(count, TestReflectionData::getPropertyCount());
- }
-
- template<> template<>
- void TestReflectionObject::test<10>()
- {
- // Check meta classes of different types do not compare equal.
- const LLMetaClass* reflection_data_meta_class = &(LLMetaClassT<TestReflectionData>::instance());
- const LLMetaClass* aggregated_data_meta_class = &(LLMetaClassT<TestAggregatedData>::instance());
- ensure_not_equals(reflection_data_meta_class, aggregated_data_meta_class);
- }
-
- template<> template<>
- void TestReflectionObject::test<11>()
- {
- // Check class cast checks.
- const LLMetaClass& meta_class = LLMetaClassT<TestReflectionData>::instance();
- TestAggregatedData* aggregated_data = new TestAggregatedData();
- LLMetaClass::PropertyIterator iter;
- U32 exception_count = 0;
- for(iter = meta_class.beginProperties(); iter != meta_class.endProperties(); ++iter)
- {
- try
- {
- const LLMetaProperty* property = (*iter).second;
- const LLReflective* reflective = property->get(aggregated_data); // Wrong reflective type, should throw exception.
-
- // useless op to get rid of compiler warning.
- reflective = NULL;
- }
- catch(...)
- {
- ++exception_count;
- }
- }
- ensure_equals(exception_count, getPropertyCount());
-
- }
-}
diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp
index dd69787a1c..2a4ed44a67 100644..100755
--- a/indra/llcommon/tests/stringize_test.cpp
+++ b/indra/llcommon/tests/stringize_test.cpp
@@ -4,31 +4,25 @@
* @date 2008-09-12
* @brief Test of stringize.h
*
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- *
- * Copyright (c) 2008-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -73,6 +67,8 @@ namespace tut
llsd["i"] = i;
llsd["d"] = d;
llsd["abc"] = abc;
+ def = L"def ghi";
+
}
char c;
@@ -82,11 +78,12 @@ namespace tut
float f;
double d;
std::string abc;
+ std::wstring def;
LLSD llsd;
};
typedef test_group<stringize_data> stringize_group;
typedef stringize_group::object stringize_object;
- tut::stringize_group strzgrp("stringize");
+ tut::stringize_group strzgrp("stringize_h");
template<> template<>
void stringize_object::test<1>()
@@ -98,6 +95,7 @@ namespace tut
ensure_equals(stringize(f), "3.14159");
ensure_equals(stringize(d), "3.14159");
ensure_equals(stringize(abc), "abc def");
+ ensure_equals(stringize(def), "def ghi"); //Will generate LL_WARNS() due to narrowing.
ensure_equals(stringize(llsd), "{'abc':'abc def','d':r3.14159,'i':i34}");
}
@@ -107,4 +105,20 @@ namespace tut
ensure_equals(STRINGIZE("c is " << c), "c is c");
ensure_equals(STRINGIZE(std::setprecision(4) << d), "3.142");
}
+
+ template<> template<>
+ void stringize_object::test<3>()
+ {
+ //Tests rely on validity of wstring_to_utf8str()
+ ensure_equals(wstring_to_utf8str(wstringize(c)), wstring_to_utf8str(L"c"));
+ ensure_equals(wstring_to_utf8str(wstringize(s)), wstring_to_utf8str(L"17"));
+ ensure_equals(wstring_to_utf8str(wstringize(i)), wstring_to_utf8str(L"34"));
+ ensure_equals(wstring_to_utf8str(wstringize(l)), wstring_to_utf8str(L"68"));
+ ensure_equals(wstring_to_utf8str(wstringize(f)), wstring_to_utf8str(L"3.14159"));
+ ensure_equals(wstring_to_utf8str(wstringize(d)), wstring_to_utf8str(L"3.14159"));
+ ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(L"abc def"));
+ ensure_equals(wstring_to_utf8str(wstringize(abc)), wstring_to_utf8str(wstringize(abc.c_str())));
+ ensure_equals(wstring_to_utf8str(wstringize(def)), wstring_to_utf8str(L"def ghi"));
+ // ensure_equals(wstring_to_utf8str(wstringize(llsd)), wstring_to_utf8str(L"{'abc':'abc def','d':r3.14159,'i':i34}"));
+ }
} // namespace tut
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index 1001ebc466..785197ba11 100644..100755
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -4,19 +4,52 @@
* @date 2009-03-11
* @brief Define a class useful for unit tests that engage llerrs (LL_ERRS) functionality
*
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- * Copyright (c) 2009, Linden Research, Inc.
+ * $LicenseInfo:firstyear=2009&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#if ! defined(LL_WRAPLLERRS_H)
#define LL_WRAPLLERRS_H
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+#include <tut/tut.hpp>
#include "llerrorcontrol.h"
+#include "stringize.h"
+#include <boost/bind.hpp>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+#include <list>
+#include <string>
+#include <stdexcept>
+
+// statically reference the function in test.cpp... it's short, we could
+// replicate, but better to reuse
+extern void wouldHaveCrashed(const std::string& message);
-struct WrapLL_ERRS
+struct WrapLLErrs
{
- WrapLL_ERRS():
+ WrapLLErrs():
// Resetting Settings discards the default Recorder that writes to
// stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the
// console output of successful tests, potentially confusing things.
@@ -25,10 +58,10 @@ struct WrapLL_ERRS
mPriorFatal(LLError::getFatalFunction())
{
// Make LL_ERRS call our own operator() method
- LLError::setFatalFunction(boost::bind(&WrapLL_ERRS::operator(), this, _1));
+ LLError::setFatalFunction(boost::bind(&WrapLLErrs::operator(), this, _1));
}
- ~WrapLL_ERRS()
+ ~WrapLLErrs()
{
LLError::setFatalFunction(mPriorFatal);
LLError::restoreSettings(mPriorErrorSettings);
@@ -49,8 +82,128 @@ struct WrapLL_ERRS
}
std::string error;
- LLError::Settings* mPriorErrorSettings;
+ LLError::SettingsStoragePtr mPriorErrorSettings;
LLError::FatalFunction mPriorFatal;
};
+/**
+ * Capture log messages. This is adapted (simplified) from the one in
+ * llerror_test.cpp.
+ */
+class CaptureLogRecorder : public LLError::Recorder, public boost::noncopyable
+{
+public:
+ CaptureLogRecorder()
+ : LLError::Recorder(),
+ boost::noncopyable(),
+ mMessages()
+ {
+ }
+
+ virtual ~CaptureLogRecorder()
+ {
+ }
+
+ virtual void recordMessage(LLError::ELevel level, const std::string& message)
+ {
+ mMessages.push_back(message);
+ }
+
+ /// Don't assume the message we want is necessarily the LAST log message
+ /// emitted by the underlying code; search backwards through all messages
+ /// for the sought string.
+ std::string messageWith(const std::string& search, bool required)
+ {
+ for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend());
+ rmi != rmend; ++rmi)
+ {
+ if (rmi->find(search) != std::string::npos)
+ return *rmi;
+ }
+ // failed to find any such message
+ if (! required)
+ return std::string();
+
+ throw tut::failure(STRINGIZE("failed to find '" << search
+ << "' in captured log messages:\n"
+ << boost::ref(*this)));
+ }
+
+ std::ostream& streamto(std::ostream& out) const
+ {
+ MessageList::const_iterator mi(mMessages.begin()), mend(mMessages.end());
+ if (mi != mend)
+ {
+ // handle first message separately: it doesn't get a newline
+ out << *mi++;
+ for ( ; mi != mend; ++mi)
+ {
+ // every subsequent message gets a newline
+ out << '\n' << *mi;
+ }
+ }
+ return out;
+ }
+
+private:
+ typedef std::list<std::string> MessageList;
+ MessageList mMessages;
+};
+
+/**
+ * Capture log messages. This is adapted (simplified) from the one in
+ * llerror_test.cpp.
+ */
+class CaptureLog : public boost::noncopyable
+{
+public:
+ CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG)
+ // Mostly what we're trying to accomplish by saving and resetting
+ // LLError::Settings is to bypass the default RecordToStderr and
+ // RecordToWinDebug Recorders. As these are visible only inside
+ // llerror.cpp, we can't just call LLError::removeRecorder() with
+ // each. For certain tests we need to produce, capture and examine
+ // DEBUG log messages -- but we don't want to spam the user's console
+ // with that output. If it turns out that saveAndResetSettings() has
+ // some bad effect, give up and just let the DEBUG level log messages
+ // display.
+ : boost::noncopyable(),
+ mOldSettings(LLError::saveAndResetSettings()),
+ mRecorder(new CaptureLogRecorder())
+ {
+ LLError::setFatalFunction(wouldHaveCrashed);
+ LLError::setDefaultLevel(level);
+ LLError::addRecorder(mRecorder);
+ }
+
+ ~CaptureLog()
+ {
+ LLError::removeRecorder(mRecorder);
+ LLError::restoreSettings(mOldSettings);
+ }
+
+ /// Don't assume the message we want is necessarily the LAST log message
+ /// emitted by the underlying code; search backwards through all messages
+ /// for the sought string.
+ std::string messageWith(const std::string& search, bool required=true)
+ {
+ return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->messageWith(search, required);
+ }
+
+ std::ostream& streamto(std::ostream& out) const
+ {
+ return boost::dynamic_pointer_cast<CaptureLogRecorder>(mRecorder)->streamto(out);
+ }
+
+private:
+ LLError::SettingsStoragePtr mOldSettings;
+ LLError::RecorderPtr mRecorder;
+};
+
+inline
+std::ostream& operator<<(std::ostream& out, const CaptureLogRecorder& log)
+{
+ return log.streamto(out);
+}
+
#endif /* ! defined(LL_WRAPLLERRS_H) */
diff --git a/indra/llcommon/timer.h b/indra/llcommon/timer.h
index 21ed06adc9..82d19c2d32 100644..100755
--- a/indra/llcommon/timer.h
+++ b/indra/llcommon/timer.h
@@ -2,31 +2,25 @@
* @file timer.h
* @brief Legacy wrapper header.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "lltimer.h"
diff --git a/indra/llcommon/timing.cpp b/indra/llcommon/timing.cpp
index e1c1f6dc92..c2dc695ef3 100644..100755
--- a/indra/llcommon/timing.cpp
+++ b/indra/llcommon/timing.cpp
@@ -2,30 +2,24 @@
* @file timing.cpp
* @brief This file will be deprecated in the future.
*
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
diff --git a/indra/llcommon/timing.h b/indra/llcommon/timing.h
deleted file mode 100644
index 140ce1fcaa..0000000000
--- a/indra/llcommon/timing.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * @file timing.h
- * @brief Cross-platform routines for doing timing.
- *
- * $LicenseInfo:firstyear=2000&license=viewergpl$
- *
- * Copyright (c) 2000-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_TIMING_H
-#define LL_TIMING_H
-
-
-#if LL_LINUX || LL_DARWIN || LL_SOLARIS
-#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;
-
-// functionality has been moved lltimer.{cpp,h}. This file will be deprecated in the future.
-
-#endif
diff --git a/indra/llcommon/u64.cpp b/indra/llcommon/u64.cpp
index 01fe973a22..02c2c15d26 100644..100755
--- a/indra/llcommon/u64.cpp
+++ b/indra/llcommon/u64.cpp
@@ -2,31 +2,25 @@
* @file u64.cpp
* @brief Utilities to deal with U64s.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
@@ -42,7 +36,7 @@ U64 str_to_U64(const std::string& str)
if (!aptr)
{
- llwarns << "str_to_U64: Bad string to U64 conversion attempt: format\n" << llendl;
+ LL_WARNS() << "str_to_U64: Bad string to U64 conversion attempt: format\n" << LL_ENDL;
}
else
{
diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h
index eb51131e94..75c8a59136 100644..100755
--- a/indra/llcommon/u64.h
+++ b/indra/llcommon/u64.h
@@ -2,31 +2,25 @@
* @file u64.h
* @brief Utilities to deal with U64s.
*
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
* Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
*
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
*
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/