User:Luisa Bourgoin/current projects/memory sim

From Second Life Wiki
Jump to navigation Jump to search

Usually you need some 1024 bytes of data storage inside a script, no more. Back in the days before MONO arrived, there has been a memory limitation of 16Kb for single script. Script source getting stored inside a script object, too - so the bigger your script has been, the lesser memory there has been left for dynamically assigned data.

One of the reasons why I started squishing script source into denser packages.

Since MONO, limits had been raised up onto 64Kb. If that still doesn't suit you, try separating script intelligence and data storage into two parts. I have gone further, separating into a memory manager and some storage scripts (ten, twelve).

This code has not been much in use since the old days, and needs a fix. I am most recently just using a single memory sim script and connect from main script directly, without a central manager in between.

Stuff that is missing now:

  • changes for best MONO coompatibility. right now it compiles, and works, but stringing up lists has been optimized for wonky ole LSL
  • uniqueness of sim_id isn't granted, statistical chance of failure remaining

memory sim
drag two or three inside a cube. each stores some amount of data

<lsl>// (c) Luisa Bourgoin March 2007 // // memory-sims scripted in LSL // // * llGetFreeMemory() still broken, call Linden Labs ;-)

list heap = []; list keys = []; key sim_id = NULL_KEY; integer DEBUG = FALSE;

integer reserved_free = 1024; integer current_free = 0;

integer STORE_REQUEST = 8430001; integer STORE_DATA = 8430002; integer STORE_ACK = 8430003; integer DATA_REQUEST = 8430004; integer DATA_DELIVERY = 8430005; integer DELETE_REQUEST= 8430006; integer SEARCH_KEYS = 8430007; integer SEARCH_DATA = 8430008; integer STATS_REQUEST = 8430009; integer STATS_DELIVERY = 8430010; integer DUMP_CSV = 8430011;

integer store_data(string id, string data) {

   //key has been deleted before, in every case.
   integer length = 0;
   heap = [data] + heap;
   keys = [id] + keys;
   length = llStringLength(data) + llStringLength(id);
   //debug((string)sim_id+" storing id="+id+" mem="+(string)length);
   return( length );

}

string recall_data(string id) {

   integer adress = -1;
   adress = llListFindList( keys, [id] );
   if( adress >= 0 )
   {
       return( llList2String( heap, adress ) );
   }
   else {
       return("");
   }

}

list search_keys(string str) {

   list results = [];
   integer i;
   for( i=0; i<llGetListLength(keys); i++ )
   {
       if( llSubStringIndex( llList2String(keys,i), str) >= 0 )
       {
           results = llList2String(keys,i) + results;
       }
   }
   return(results);

}

search_data(string str) {

   string results = "";
   integer idx = -1;
   integer i;
   for( i=0; i<llGetListLength(heap); i++ )
   {
       idx = llSubStringIndex( llList2String(heap,i), str);
       if( idx >= 0 ) {
           results = "found in: "+llList2String(keys,i)+" at "+(string)idx+"\n";
       }
   }
   if( results != "" ) {
       llMessageLinked(LINK_THIS, DATA_DELIVERY, results, (key)"search found");
   }

}

integer delete_data(string id) {

   integer length = 0;
   integer adress = -1;
   adress = llListFindList( keys, [id] );
   if( adress >= 0 )
   {
       length = llStringLength(llList2String( keys, adress )) + llStringLength(llList2String( heap, adress ));
       heap = llDeleteSubList( heap, adress, adress );
       keys = llDeleteSubList( keys, adress, adress );
   }
   return(length);

}

list gather_statistics() {

   integer number = llGetListLength(keys);
   integer used = 0;
   integer data = 0;
   integer i;
   for( i=0; i<llGetListLength(heap); i++ )
   {
        used = used + llStringLength(llList2String( keys, i )) + llStringLength(llList2String( heap, i ));
        data = data + llStringLength(llList2String( heap, i ));
   }
   return([current_free, used, number, data]);

}

dump_storage() {

   integer i=0;
   string name = llGetObjectName();
   if( llGetListLength(keys) > 0 )
   {
       while (i < llGetListLength(keys))
       {
           llSetObjectName( llList2String(keys,i) );
           llOwnerSay( "/me == "+llList2String(heap,i) );
           i++;
       }
       llSetObjectName( name );
   }

}

debug(string message) { if(DEBUG) { llOwnerSay(message); } }

default {

   state_entry()
   {
       // random memory sim address
       integer rand = (integer)llFrand(8430000) + 2000000;
       string tmp = (string)rand;
       sim_id = (key)tmp;  // casting&cursing ;-)
       current_free = llGetFreeMemory();
   }
   
   link_message(integer sender_num, integer num, string str, key id) {
       if( num == DATA_REQUEST )
       {
           string tmp = recall_data((string)id);
           if( tmp != "" ) {
               llMessageLinked(LINK_THIS, DATA_DELIVERY, tmp, id);
               tmp = "";
           }
       }
       else if( num == STORE_REQUEST )
       {
           current_free = current_free + delete_data((string)id); //it's expired by this request, better remove !
           integer size = (integer)str; //str carries an integer in this message, be aware !
           integer free = current_free - size;
           if( free > reserved_free ) {
               llMessageLinked(LINK_THIS, STORE_ACK, (string)free, sim_id); //key field used for sim_id !
           }
       }
       else if( (string)num == (string)sim_id ) //STORE_DATA goes at specific sim_id
       {
           current_free = current_free - store_data((string)id,str);
       }
       else if( num == DELETE_REQUEST )
       {
           current_free = current_free + delete_data((string)id);
       }
       else if( num == SEARCH_KEYS )
       {
           if( (string)id == "" && llGetListLength(keys) > 0 ) {
               llMessageLinked(LINK_THIS, DATA_DELIVERY, llDumpList2String(keys,","), (key)"entries found");
           } else {
               list results = search_keys((string)id);
               if( llGetListLength(results) > 0 ) {
                   llMessageLinked(LINK_THIS, DATA_DELIVERY, llDumpList2String(results,","), (key)"entries found");
                   results = [];
               }
           }
       }
       else if( num == SEARCH_DATA )
       {
           search_data((string)id);
       }
       else if( num == STATS_REQUEST )
       {
           list stats = gather_statistics();
           llMessageLinked(LINK_THIS, STATS_DELIVERY, llDumpList2String(stats,","), sim_id);
       }
       else if( num == DUMP_CSV )
       {
           dump_storage();
       }
   }

} </lsl>


memory manager (including command line shell)
manages data delivery and storage into several memory sims

<lsl>// (c) Luisa Bourgoin March 2007 // // memory-sims-manager script in LSL //------------------------------------ // * currently string storage only. // call list2string() / string2list() for capsulating other data types

integer SHELL = TRUE; integer lineLength = 120; integer PUBLIC = FALSE; integer DEBUG = FALSE;

list sim_ids = []; integer free_storage = 0; integer sim_number_installed = 0; list pending_stores = []; list pending_stats = []; list collected_stats = [0,0,0,0]; string hostname = ""; integer LISTEN_HANDLE = -1;

//constants: integer STORE_REQUEST = 8430001; integer STORE_DATA = 8430002; integer STORE_ACK = 8430003; integer DATA_REQUEST = 8430004; integer DATA_DELIVERY = 8430005; integer DELETE_REQUEST= 8430006; integer SEARCH_KEYS = 8430007; integer SEARCH_DATA = 8430008; integer STATS_REQUEST = 8430009; integer STATS_DELIVERY = 8430010; integer DUMP_CSV = 8430011; integer CONNECTOR_2_MEM_MANAGER = 8430015;

store_data(string adress, string data) {

   integer length = llStringLength(data);
   pending_stores = [length,adress,data] + pending_stores;
   llMessageLinked(LINK_THIS, STORE_REQUEST, (string)length, (key)adress);

}

recall_data(string adress) {

  llMessageLinked(LINK_THIS, DATA_REQUEST, "", (key)adress);

}

delete_data(string adress) {

   llMessageLinked(LINK_THIS, DELETE_REQUEST, "", (key)adress);

}

search_request(integer mode, string adress) {

   llMessageLinked(LINK_THIS, mode, "", (key)adress);

}

memory_usage() {

   llSay(0, "MEMORY USAGE");
   pending_stats = [];
   collected_stats = [0,0,0,0];
   llMessageLinked(LINK_THIS, STATS_REQUEST, "", NULL_KEY);

}

dump_storage() {

   llOwnerSay("DUMPING MEMORY IMAGE");
   llMessageLinked(LINK_THIS, DUMP_CSV, "", NULL_KEY);

}

debug(string message) { if(DEBUG) { llOwnerSay(message); } }

//that is the boot state, collecting IDs of installed components default {

   state_entry()
   {
       llSetRemoteScriptAccessPin(16986443);
       sim_number_installed = 0;
       free_storage = 0;
       sim_ids = [];
       pending_stores = [];
       hostname = llGetObjectName();
       integer i;
       for( i = llGetInventoryNumber(INVENTORY_SCRIPT); i>0; i-- )
       {
           if( llGetSubString(llGetInventoryName(INVENTORY_SCRIPT,i), 0, 9) == "memory sim" ) {
                sim_number_installed += 1;
           }
       }
       debug("memory sims found: "+(string)sim_number_installed);
       //we collect sim_ids via their STORE_ACK replies on a faked zero length STORE_REQUEST:
       i = 0; llMessageLinked(LINK_THIS, STORE_REQUEST, (string)i, (key)"");
       llSetTimerEvent(1);
   }
   
   link_message(integer sender_num, integer num, string str, key id) {
       if( num == STORE_ACK )
       {
           if( llListFindList(sim_ids, [id]) < 0 ) {
               sim_ids = [id] + sim_ids;
               free_storage = free_storage + (integer)str;
               debug("Sim ID="+(string)id+" free="+str);
               if( llGetListLength(sim_ids) == sim_number_installed )
               {
                   llSay(0, "Memory free: "+(string)free_storage+" Cache: "+(string)llGetFreeMemory());
                   state running;
               }
           }
       }
   }
   timer()
   {
       integer i = 0;
       llMessageLinked(LINK_THIS, STORE_REQUEST, (string)i, NULL_KEY);
   }

}

state running {

   state_entry()
   {
       if( SHELL )
       {
           llListenRemove( LISTEN_HANDLE );
           if( PUBLIC ) {
               LISTEN_HANDLE = llListen(0, "", NULL_KEY, "");
           } else {
               LISTEN_HANDLE = llListen(0, "", llGetOwner(), "");
           }
       }
   }
   link_message(integer sender_num, integer num, string str, key id) {
       if( num == STORE_ACK & llGetListLength(pending_stores) >= 0 )
       {
           integer size = (integer)str;
           string sim_id = (string)id;
           integer pos = -1;
           integer i;
           for( i=0; i<llGetListLength(pending_stores); i=i+3 ) {
               if( llList2Integer(pending_stores, i) <= size ) {
                   pos = i; // FIFO since we prepend new ones to the left
               }
           }
           if( pos >= 0 ) {
               string adress = llList2String(pending_stores,pos + 1);
               string data = llList2String(pending_stores,pos + 2);
               pending_stores = llDeleteSubList( pending_stores, pos, pos + 2 );
               llMessageLinked(LINK_THIS, (integer)sim_id, data, (key)adress); //STORE_DATA
           }
       }
       else if( num == DATA_DELIVERY )
       {
           if( SHELL ) {
               llSetObjectName(">");
               llSay(0, "/me "+(string)id+" == "+llGetSubString(str,0,lineLength) );
               str = llDeleteSubString(str,0,lineLength);
               while( llStringLength(str) > 0 ) {
                   llSay(0, "/me "+llGetSubString(str,0,lineLength));
                   str = llDeleteSubString(str,0,lineLength);
               }
               llSetObjectName(hostname);
           }
       }
       else if( num == CONNECTOR_2_MEM_MANAGER )
       {
           store_data((string)id, str);
       }
       else if( num == STATS_DELIVERY ) {
          debug("current_free/used/number/data: "+str);
          if( llListFindList(pending_stats, [id]) < 0 ) {
               pending_stats = [id] + pending_stats;
               list stats_reported = llParseString2List(str, [","], []);
               integer i; integer new;
               for( i=0; i<4; i++) {  //this *is* awfull
                   new = llList2Integer(collected_stats, i) + llList2Integer(stats_reported, i);
                   collected_stats = llListReplaceList( collected_stats, [new], i, i ); //awww, *awfull*
               }
               if( llGetListLength(pending_stats) == sim_number_installed ) {
                   llSay(0, "total_free/used/number/data: "+llDumpList2String(collected_stats,","));
               }
           }
       }
       str = ""; id = NULL_KEY;
   }
   // if the shell/chat interface is activated
   listen( integer channel, string name, key id, string message)
   {
       string adress = "";
       string data = "";
       list line = llParseStringKeepNulls(message, [" "], []);
       string command = llList2String(line, 0);
       if( command == "S" ) {
           adress = llList2String(line, 1);
           data = llDumpList2String(llList2List(line,2,-1), " ");
           llSay(0, "/me stores: "+adress+" == "+data);
           store_data(adress, data);
       }
       else if( command == "R" ) {
           adress = llDumpList2String(llList2List(line,1,-1), " ");
           //llSay(0, "/me recalls: "+adress);
           recall_data(adress);
       }
       else if( command ==  "D" ) {
           adress = llDumpList2String(llList2List(line,1,-1), " ");
           llSay(0, "/me deletes: "+adress);
           delete_data(adress);
       }
       else if( command == "L" ) {
           if(llGetListLength(line) != 1) {
               adress = llDumpList2String(llList2List(line,1,-1), " ");
               llSay(0, "list '"+adress+"'");
           } else {
               adress = "";
           }
           search_request(SEARCH_KEYS, adress);
       }
       else if( command ==  "RESET" ) {
           //and that talks about memory usage, too
           if( llGetListLength(pending_stores) == 0 ) {
               state default;
           } else {
               llSay(0, "Nope, pending stores!");
           }
       }
       else if( command ==  "F" ) {
           adress = llDumpList2String(llList2List(line,1,-1), " ");
           search_request(SEARCH_DATA, adress);
       }
       else if( command ==  "FREE" ) {
           memory_usage();
       }
       else if( command ==  "HELP" ) {
           llSay(0,"use 'S key data' to store data under key");
           llSay(0,"'R key' to regain them");
           llSay(0,"'D key' deletes");
           llSay(0,"'L' or 'L keypart' for a listing");
           llSay(0,"'FREE' gives usage stats");
           llSay(0,"'F data' does a whole search in dataspace");
       }
       else if( command ==  "DUMP" ) {
           dump_storage();
       }
   }

} </lsl>