Jump to content

Basic LinksetData Statistics


Fenix Eldritch
 Share

You are about to reply to a thread that has been inactive for 360 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Here's a very simple tool for looking at some basic info about an object's LinksetData store. It will report how many entries the datastore contains (both protected and non-protected) as well as the total size used in bytes. It can give a simplistic breakdown of how the size is allocated between KVPs and protected overhead.

If the rumors of LL considering a form of GUI for LSD management turn out to be true, then this may become obsolete. But until then, maybe it'll be somewhat useful.

/*************************************************\
Drop this script into an object and it will scan
the LinksetData store if it exists and report some
statistics.
\*************************************************/

string tempKey = "LSD temporary byte count key";    //used to count the bytes of keys/values (28 bytes)


default
{
    state_entry()
    {
        integer tempA;                      //used for counting bytes
        integer tempB;                      //used for counting bytes
        
        integer LSD_MAX_SIZE = 65536;   //current max is 64k
        integer countTotal = llLinksetDataCountKeys();
        integer countProtect;
        
        integer bytesTotal  = LSD_MAX_SIZE - llLinksetDataAvailable();
        integer bytesPubKeys;
        integer bytesPrivKeys;
        integer bytesPubValues;
        
        integer largestRecordChr;
        integer largestKeyChr;
        integer smallestRecordChr = 0x7FFFFFFF;    //init to max positive int
        integer smallestKeyChr = 0x7FFFFFFF;       //init to max positive int
        
        integer largestRecordByte;
        integer largestKeyByte;
        integer smallestRecordByte = 0x7FFFFFFF;    //init to max positive int
        integer smallestKeyByte = 0x7FFFFFFF;       //init to max positive int
        
        integer i = countTotal;
        while (~--i)    //this creates a range from count-1 to 0. Great for zero-indexed lists.
        {
            string currKey = llList2CSV(llLinksetDataListKeys(i,1));
            string currData = llLinksetDataRead(currKey);
            
            //llOwnerSay((string)i+"|"+currKey+"|"+currData); //DEBUG
            
            
            llLinksetDataWrite(tempKey,currKey);    //tempKey by itself is 28 bytes
            tempA = LSD_MAX_SIZE - llLinksetDataAvailable() - bytesTotal - 28;
            llLinksetDataWrite(tempKey,currData);    //tempKey by itself is 28 bytes
            tempB = LSD_MAX_SIZE - llLinksetDataAvailable() - bytesTotal - 28;
            llLinksetDataDelete(tempKey);
            
            if(currData)    //if data isn't empty, KVP was non protected
            {
                bytesPubKeys += tempA;      //add to running key byte total
                if(tempA > largestKeyByte)  //check if this is the biggest key so far
                {
                    largestKeyByte = tempA;
                    largestKeyChr = llStringLength(currKey);
                }
                if(tempA < smallestKeyByte)    //check if this is the samllest key so far
                {
                    smallestKeyByte = tempA;
                    smallestKeyChr = llStringLength(currKey);
                }
                
                bytesPubValues += tempB;      //add to running record byte total
                if(tempB > largestRecordByte)  //check if this is the biggest record so far
                {
                    largestRecordByte = tempB;
                    largestRecordChr = llStringLength(currData);
                }
                if(tempB < smallestRecordByte)    //check if this is the samllest record so far
                {
                    smallestRecordByte = tempB;
                    smallestRecordChr = llStringLength(currData);
                }
            }
            else        //if data was empty, then the value was protected
            {
                countProtect++;
                bytesPrivKeys += tempA;
                if(tempA > largestKeyByte)  //check if this is the biggest key so far
                {
                    largestKeyByte = tempA;
                    largestKeyChr = llStringLength(currKey);
                }
                if(tempA < smallestKeyByte)    //check if this is the samllest key so far
                {
                    smallestKeyByte = tempA;
                    smallestKeyChr = llStringLength(currKey);
                }
            }
        }
        
        llOwnerSay("LinksetData statisitcs:"
            +"\n Total entries: "+(string)countTotal+" ("+(string)countProtect+" protected)"
            +"\n Total size: "+(string)(bytesTotal)+" out of 65536 bytes used"
            +"\n ├Public keys: "+(string)bytesPubKeys+" bytes"
            +"\n ├Public data: "+(string)bytesPubValues+" bytes"
            +"\n ├Protected keys: "+(string)bytesPrivKeys+" bytes"
            +"\n ├Protected data: "+(string)(bytesTotal - bytesPrivKeys - bytesPubValues - bytesPubKeys - (countProtect*32))+" bytes"
            +"\n └Protected overhead: "+(string)(countProtect*32)+" bytes"
            +"\n Largest key: "+(string)largestKeyByte+" bytes ("+(string)largestKeyChr+" characters)"
            +"\n Smallest key: "+(string)smallestKeyByte+" bytes ("+(string)smallestKeyChr+" characters)"
            +"\n Largest public data entry: "+(string)largestRecordByte+" bytes ("+(string)largestRecordChr+" characters)"
            +"\n Smallest public data entry: "+(string)smallestRecordByte+" bytes ("+(string)smallestRecordChr+" characters)"
            +"\nNOTE: each protected key:value pair adds 32 bytes to the datastore usage."
        );
    }
}

 

 

Edited by Fenix Eldritch
version 2
  • Like 2
Link to comment
Share on other sites

Updated the OP with a new version. I realized I could further break down the byte counts with a single sacrificial LSD entry to measure key and value sizes. Although we can't read protected entries, knowing the other sizes and the protected overhead, we can infer how many bytes are being used for protected data entries.

Link to comment
Share on other sites

A minor bug for an empty datastore:

Quote

 Largest key: 0 bytes (0 characters)
 Smallest key: 2147483647 bytes (2147483647 characters)

Also, to make it fire after changes to the datastore (useful for rapid sanity-testing/debugging), you can swap to a different state so it doesn't interfere with itself:

// code as above until:
        llOwnerSay("LinksetData statisitcs:"
// ...
        );
        state waiting;
    }
}
state waiting
{   linkset_data(integer action, string name, string value)
    {   state default;
    }
}

 

  • Like 2
Link to comment
Share on other sites

17 minutes ago, Quistess Alpha said:

A minor bug for an empty datastore

Well shoot. And I'm past the edit window. Numerous ways to handle that, but a quick way would be to put something like this just before the final output:

if(!countTotal)
{
    smallestKeyChr = 0;
    smallestKeyByte = 0;
    smallestRecordChr = 0;
    smallestRecordByte = 0;
}

 

  • Like 2
Link to comment
Share on other sites

  • 4 months later...

Made a few optimizations and added a few more statistics. And accounted for the recent datastore size increase to 128k. The script will also early abort if the datstore is empty.

/*************************************************\
Drop this script into an object and it will scan
the LinksetData store if it exists and report some
statistics.
\*************************************************/

integer LSD_MAX_SIZE = 131072;   //current max is 128k. Update this when/if the max is increased.
integer bytesTotal;
string tempKey = "LSD temporary byte count key";    //used to count the bytes of keys/values (28 bytes)

//calculate what % input is of bytesTotal and output to specified precision.
//https://wiki.secondlife.com/wiki/Fixed_Precision
string int2PercentStr(integer in, integer precision)
{
    float x = ((float)in/(float)bytesTotal)*100;    //this will blow up if bytesTotal=0 (math error: divide by zero)
    if((precision = (precision - 7 - (precision < 1))) & 0x80000000)    //masking with 0x80000000 guards against negative index
        return llGetSubString((string)x, 0, precision);
    return (string)x;
}

default
{
    state_entry()
    {   
        integer tempA;                      //used for counting bytes
        integer tempB;                      //used for counting bytes
        
        integer countTotal = llLinksetDataCountKeys();
        integer countProtect;
        
        bytesTotal  = LSD_MAX_SIZE - llLinksetDataAvailable();
        integer bytesPubKeys;
        integer bytesPubValues;
        integer bytesPrivKeys;
        integer bytesPrivValues;
        integer bytesPrivOverhead;
        
        integer largestKeyChr;
        integer largestKeyByte;
        integer largestRecordChr;
        integer largestRecordByte;
        
        integer smallestKeyChr     = 0x7FFFFFFF;    //init to max positive int
        integer smallestKeyByte    = 0x7FFFFFFF;    //init to max positive int
        integer smallestRecordChr  = 0x7FFFFFFF;    //init to max positive int
        integer smallestRecordByte = 0x7FFFFFFF;    //init to max positive int
        
        integer i = countTotal;
        while (~--i)    //this creates a range from count-1 to 0. Great for zero-indexed lists.
        {
            string currKey = llList2CSV(llLinksetDataListKeys(i,1));
            string currData = llLinksetDataRead(currKey);
            
            //llOwnerSay((string)i+"|"+currKey+"|"+currData); //DEBUG
            
            llLinksetDataWrite(tempKey,currKey);    //tempKey by itself is 28 bytes
            tempA = LSD_MAX_SIZE - llLinksetDataAvailable() - bytesTotal - 28;
            llLinksetDataWrite(tempKey,currData);    //tempKey by itself is 28 bytes
            tempB = LSD_MAX_SIZE - llLinksetDataAvailable() - bytesTotal - 28;
            llLinksetDataDelete(tempKey);
            
            //keys are always publicly visible, only data is potentially hidden
            if(tempA > largestKeyByte)  //check if this is the biggest key so far
            {
                largestKeyByte = tempA;
                largestKeyChr = llStringLength(currKey);
            }
            if(tempA < smallestKeyByte)    //check if this is the samllest key so far
            {
                smallestKeyByte = tempA;
                smallestKeyChr = llStringLength(currKey);
            }
            
            if(currData)    //if data isn't empty, KVP was public
            {
                bytesPubKeys += tempA;          //add to running key byte total
                bytesPubValues += tempB;        //add to running record byte total
                
                if(tempB > largestRecordByte)   //check if this is the biggest record so far
                {
                    largestRecordByte = tempB;
                    largestRecordChr = llStringLength(currData);
                }
                if(tempB < smallestRecordByte)    //check if this is the samllest record so far
                {
                    smallestRecordByte = tempB;
                    smallestRecordChr = llStringLength(currData);
                }
            }
            else        //if data was empty, then the value was protected
            {
                countProtect++;
                bytesPrivKeys += tempA;
            }
        }
        
        //calculate other totals
        bytesPrivOverhead = countProtect*32;
        bytesPrivValues = bytesTotal - bytesPrivKeys - bytesPubValues - bytesPubKeys - bytesPrivOverhead;
        
        if(countTotal)
        {
            llOwnerSay("LinksetData statisitcs:"
                +"\n Total entries: "               +(string)countTotal         +" ("+(string)countProtect+" protected)"
                +"\n Total size: "                  +(string)(bytesTotal)       +" out of "+(string)LSD_MAX_SIZE+" bytes used"
                +"\n ├Public keys: "                +(string)bytesPubKeys       +" bytes ("+int2PercentStr(bytesPubKeys,3)+"%)"
                +"\n ├Public data: "                +(string)bytesPubValues     +" bytes ("+int2PercentStr(bytesPubValues,3)+"%)"
                +"\n ├Protected keys: "             +(string)bytesPrivKeys      +" bytes ("+int2PercentStr(bytesPrivKeys,3)+"%)"
                +"\n ├Protected data: "             +(string)bytesPrivValues    +" bytes ("+int2PercentStr(bytesPrivValues,3)+"%)"
                +"\n └Protected overhead: "         +(string)bytesPrivOverhead  +" bytes ("+int2PercentStr(bytesPrivOverhead,3)+"%)"
                +"\n Largest key: "                 +(string)largestKeyByte     +" bytes ("+(string)largestKeyChr+" chrs)"
                +"\n Smallest key: "                +(string)smallestKeyByte    +" bytes ("+(string)smallestKeyChr+" chrs)"
                +"\n Largest public data entry: "   +(string)largestRecordByte  +" bytes ("+(string)largestRecordChr+" chrs)"
                +"\n Smallest public data entry: "  +(string)smallestRecordByte +" bytes ("+(string)smallestRecordChr+" chrs)"
                +"\nNOTE: each protected KVP adds 32 bytes overhead."
            );
        }
        else
        {
            llOwnerSay("Object contains no linkset data.");
        }
    }
}

 

  • Thanks 2
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 360 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...