primfs_primdrive.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//                       PrimFS™ and PrimDrive™                          //
//         The Primitive Filesystem for the Primitive Hard Drive.        //
///////////////////////////////////////////////////////////////////////////
// PrimFS™ and PrimDrive™ introduce an unconventional hard-drive that is
// able to store data in three-dimensions instead of two. While
// conventional hard-drives use superposed disks, PrimDrive™ uses a cube
// that is physically sub-divided into smaller blocks called sectors.
//
// Storing data in three dimensions instead of two allows for some helpful
// features, namely:
//   - each sector can change its own color to indicate how much data
//      has been written to it and also how much free space is available
//   - by consequence, looking at the PrimDrive™ will immediatly give
//      an overview of the clustering of data.
//
// Wizardry and Steamworks uses key-value data as the underlying data
// representation. The simplicity is desirable such that sectors can hold
// as much data as possible without too much structure overhead - by
// contrast with llJson* functions, for example.
//
// Due to the fact that each block is a physical object some interesting
// qualities can be implicitly observed, for instance:
//   - defragmenting the drive could be a mechanical operation (optical?)
//      that the filesystem (PrimFS™) could not even bother with.
//   - The PrimDrive™ could be extended as easily as attaching sectors.
//   - The PrimDrive™ could be shrunk as easily as detaching sectors.
//   - Sectors could be shared between PrimDrive™s just by detaching and
//      attaching them to other PrimDrive™s. PrimFS™ always uses just as
//      much sectors space _as needed_ such that it is easy (perhaps, out-
//      of-code) to identify unoccupied sectors and perform operations
//      them.
//
// Traditional drives go from the mathematical property of circles that
// have the maximal surface at the minimal perimeter. That is, no other
// shape can have a surface larger than a circle at a given perimeter.
//
// Nevertheless, the idea of disks are far too immature for a 3D world
// where data could be stored on three axes instead of two.
//
// Generalizing the PrimFS™ and PrimDrive™ to a real-adaptable scenario
// would be a matter of finding a shape that is closest to an inverse-
// sphere and then disposing sectors on its surface.
//
///////////////////////////////////////////////////////////////////////////
// Capacity = [1] 127 bytes (including descriptors) / sector
//
// Implemented Operations: Write, Read, Delete, Format
// Planned Operations: Defragment, (Extend, Shrink)
//
// [1] For Linden Labs-VW style implementations.
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//                      Second Life Constants                            //
//                   PrimFS™ Physical Parameters.                        //
///////////////////////////////////////////////////////////////////////////
 
// The maximum number of bytes that we can store in a description is set
// here to 127 bytes which is the current number of bytes that you can
// store in a primitive's description in Second Life.
integer BYTES_PER_SECTOR = 127;
// Primitives can have a zero-length description but that can only be
// accomplished by setting the description to the empty string using a
// script. Otherwise, any primitive created with actually have this
// string in the description indicating that no description is set.
string UNFORMATTED_MARKER = "(No Description)";
 
///////////////////////////////////////////////////////////////////////////
//                      Wizardry and Steamworks                          //
//          http://grimore.org/fuss/data_structures/key-value_pairs      //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    list a = llParseStringKeepNulls(data, ["&", "="], []);
    integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
    if(i != -1) return llList2String(a, 2*i+1);
    return "";
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueSet(string k, string v, string data) {
    if(llStringLength(data) == 0) return k + "=" + v;
    if(llStringLength(k) == 0) return "";
    if(llStringLength(v) == 0) return "";
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []),
            0, -1, 2
        ),
    [ k ]);
    if(i != -1) return llDumpList2String(
        llListReplaceList(
            llParseString2List(data, ["&"], []),
            [ k + "=" + v ],
        i, i),
    "&");
    return data + "&" + k + "=" + v;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueDelete(string k, string data) {
    if(llStringLength(data) == 0) return "";
    if(llStringLength(k) == 0) return "";
    integer i = llListFindList(
        llList2ListStrided(
            llParseString2List(data, ["&", "="], []),
            0, -1, 2
        ),
    [ k ]);
    if(i != -1) return llDumpList2String(
        llDeleteSubList(
            llParseString2List(data, ["&"], []),
        i, i),
    "&");
    return data;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
list wasKeyValueGetKeys(string data) {
    return llList2ListStrided(
        llParseString2List(
            data, 
            ["&", "="], 
            []
        ), 
        0, 
        -1, 
        2
    );
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// returns the size of a linked primitive description
string wasGetLinkDescription(integer link) {
    string d = (string)llGetLinkPrimitiveParams(link, [PRIM_DESC]);
    if(d == UNFORMATTED_MARKER || d == "") return "";
    return d;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// sets the description of a linked primitive
wasSetLinkDescription(integer link, string description) {
    llSetLinkPrimitiveParamsFast(link, [PRIM_DESC, description]);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
vector wasPercentToGradientBrighness(float percent, vector from, vector to) {
    return <from.x + (to.x-from.x)*percent/100,
        from.y + (to.y-from.y)*percent/100,
        from.z + (to.z-from.z)*percent/100>;
}
 
///////////////////////////////////////////////////////////////////////////
//                           PrimFS™ API                                 //
//          http://grimore.org/secondlife/primfs_and_primdrive           //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Remove all descriptors k and all data associated with descriptor k
// from head to tail and return the number of bytes deleted (excluding
// the length of the descriptor k).
integer wasPrimFSDelete(string k, integer head, integer tail) {
    integer b;
    do {
        string d = wasGetLinkDescription(tail);
        if(llStringLength(d) == 0) jump continue;
        b += llStringLength(wasKeyValueGet(k, d));
        wasSetLinkDescription(tail,
            wasKeyValueDelete(
                k,
                d
            )
        );
        // GC
        d = "";
@continue;
    } while(--tail>=head);
    return b;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Returns a list containing all unique file descriptors.
list wasPrimFSGetDescriptors(integer head, integer tail) {
    list descriptors = [];
    do {
        string d = wasGetLinkDescription(tail);
        if(llStringLength(d) == 0) jump continue;
        list c = llList2ListStrided(
            llParseString2List(d, ["&", "="], []),
            0, -1, 2
        );
        // GC
        d = "";
        do {
            string k = llList2String(c, 0);
            if(llListFindList(
                descriptors, (list)k
                    ) != -1) jump skip;
            descriptors += k;
            // GC
            k = "";
@skip;
            c = llDeleteSubList(c, 0, 0);
        } while(llGetListLength(c) != 0);
        // GC
        c = [];
@continue;
    } while(--tail>=head);
    return descriptors;
}
 
///////////////////////////////////////////////////////////////////////////
//                        PrimFS™ Read-Write                             //
//                         Descriptor Level.                             //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Formats all blocks for PrimFS™ between head and tail and returns the
// number of sectors for the new partition.
// The resulting formatted partions can be found between head and tail.
integer wasPrimFSFormat(integer head, integer tail) {
    integer seek = tail;
    do {
        wasSetLinkDescription(seek, "");
    } while(--seek>=head);
    return tail-seek;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Writes data to the filesystem in the partition between ead and tail
// and returns the number of written bytes.
//
// data cannot contain the = (equal, ASCII 061) sign, a restriction of
// the key-value structure (http://grimore.org/secondlife:key_value_data).
integer wasPrimFSWrite(string k, string data, integer head, integer tail) {
    integer b;
    do {
        string d = wasGetLinkDescription(tail);
        string v = "";
        if(llSubStringIndex(d, k) != -1)
            v = wasKeyValueGet(k, d);
        integer s = llStringLength(v);
        // if the key was found
        if(s != 0) {
            s = BYTES_PER_SECTOR - llStringLength(d) + s;
            v = llGetSubString(data, 0, s-1);
            if(llStringLength(v) != 0) jump write;
            wasSetLinkDescription(tail,
                wasKeyValueDelete(
                    k,
                    d
                )
            );
            jump continue;
        }
        // if the key was not found
        if(llStringLength(d) >= BYTES_PER_SECTOR) jump continue;
        s = BYTES_PER_SECTOR - llStringLength(d) - llStringLength(k) - 1;
        // &
        if(llStringLength(d) != 0) --s;
        v = llGetSubString(data, 0, s-1);
        if(llStringLength(v) == 0) jump continue;
        // write
@write;
        b += llStringLength(v) + llStringLength(k) + 1;
        v = wasKeyValueSet(k, v, d);
        wasSetLinkDescription(tail,v);
        // GC
        v = "";
        data = llDeleteSubString(data, 0, s-1);
@continue;
        // GC
        d = "";
    } while(--tail>=head);
    // return written bytes
    return b;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Reads the filesystem on the partition between head and tail and returns
// the data associated with the file descriptor k.
string wasPrimFSRead(string k, integer head, integer tail) {
    string output = "";
    do {
        string d = wasGetLinkDescription(tail);
        if(llStringLength(d) == 0) jump continue;
        if(llListFindList(wasKeyValueGetKeys(d), [ k ]) == -1)
            jump continue;
        output += wasKeyValueGet(k, d);
@continue;
        // GC
        d = "";
    } while(--tail>=head);
    return output;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Returns the number of free space (in bytes) on a partition bounded
// by head and tail
integer wasPrimFSGetFreeSpace(integer head, integer tail) {
    integer occupied = 0;
    integer seek = tail;
    do {
        occupied += llStringLength(wasGetLinkDescription(seek));
    } while(--seek>=head);
    return BYTES_PER_SECTOR*(tail-head)-occupied;
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Returns the number of free space (in bytes) on a partition bounded
// by head and tail
integer wasPrimFSGetUsedSpace(integer head, integer tail) {
    integer occupied = 0;
    do {
      occupied += llStringLength(wasGetLinkDescription(tail));
    } while(--tail>=head);
    return occupied;
}
 
///////////////////////////////////////////////////////////////////////////
//                           PrimDrive™ API                              //
//                            Drive Level.                               //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
// Can be called after "update" (write or append) operations on the
// PrimDrive™ in order to highlight the data-consumption and clustering
// of sectors.
wasPrimDriveLED(integer head, integer tail) {
    // when tail overlaps head, we're done
    if(tail < head) return;
    string d = wasGetLinkDescription(tail);
    // indicate unformated blocks in red
    if(d == UNFORMATTED_MARKER) {
        llSetLinkPrimitiveParamsFast(tail, [
            PRIM_COLOR,
            ALL_SIDES,
            <1,0,0>,
            1]
        );
        jump continue;
    }
    integer size = llStringLength(d);
    // GC
    d = "";
    llSetLinkPrimitiveParamsFast(tail, [
        PRIM_COLOR,
        ALL_SIDES,
        wasPercentToGradientBrighness(
            100*size/(float)BYTES_PER_SECTOR,
            <1,1,1>, ZERO_VECTOR),
        .17 + size/(float)BYTES_PER_SECTOR]
    );
@continue;
    // GC
    d = "";
    wasPrimDriveLED(head, --tail);
}
 
///////////////////////////////////////////////////////////////////////////
 
list commands = [
            "write",
            "read",
            "format",
            "delete",
            "getfreespace",
            "getusedspace",
            "getdescriptors"
        ];
 
list commandStack = [];
string commandInterpreter(list input) {
 
    if(llGetListLength(input) == 0) {
 
        // pop command
        string operation = llList2String(commandStack, 0);
        commandStack = llDeleteSubList(commandStack, 0, 0);
 
        //pop head and tail off the stack
        integer tail = llList2Integer(commandStack, -1);
        integer head = llList2Integer(commandStack, -2);
        integer drive_size = llGetNumberOfPrims();
 
        //drive
        if(tail == 0 || tail > drive_size || head < 2) {
            tail = drive_size;
            head = 2;
            jump operations;
        }
        commandStack = llDeleteSubList(commandStack, -2, -1);
 
@operations;
        string output = "";
 
        // perform operations
        if(operation == "write") {
            // pop key
            string k = llList2String(commandStack, 0);
            commandStack = llDeleteSubList(commandStack, 0, 0);
 
            // got a key but no values
            if(llGetListLength(commandStack) == 0) jump feedback;
 
            output = (string)wasPrimFSWrite(
                k,
                llDumpList2String(commandStack, " "),
                head,
                tail
            );
            wasPrimDriveLED(head, tail);
            jump feedback;
        }
        if(operation == "read") {
            output = wasPrimFSRead(
                llList2String(commandStack, 0),
                head,
                tail
            );
            jump feedback;
        }
 
        if(operation == "format") {
            output = (string)wasPrimFSFormat(head, tail);
            wasPrimDriveLED(head, tail);
            jump feedback;
        }
 
        if(operation == "delete") {
            output = (string)wasPrimFSDelete(
                llList2String(commandStack, 0),
                head,
                tail
            );
            wasPrimDriveLED(head, tail);
            jump feedback;
        }
 
        if(operation == "getusedspace") {
            output = (string)wasPrimFSGetUsedSpace(head, tail);
            jump feedback;
        }
 
        if(operation == "getfreespace") {
            output = (string)wasPrimFSGetFreeSpace(head, tail);
            jump feedback;
        }
 
        if(operation == "getdescriptors") {
            output = llList2CSV(wasPrimFSGetDescriptors(head, tail));
            jump feedback;
        }
 
@feedback;
        commandStack = [];
        return output;
    }
 
    // pop input
    string i = llList2String(input, 0);
    input = llDeleteSubList(input, 0, 0);
 
    // check for valid command
    if(llListFindList(
        commands, (list)i) != -1) {
        commandStack += i;
        jump continue;
    }
 
    // check for numbers
    if(llListFindList(
        commands,
        llList2List(commandStack, 0, 0)) != -1) {
 
        integer o = (integer)i;
        // if not an integer, it's a string
        if(o == 0) {
            commandStack += i;
            jump continue;
        }
        // otherwise an integer
        commandStack += o;
 
    }
 
@continue;
    return commandInterpreter(input);
}
 
string URL = "";
 
default {
    state_entry() {
        llRequestURL();
    }
    http_request(key id, string method, string body) {
        if(method == URL_REQUEST_GRANTED) {
            URL = body;
            llSay(0, "My URL is: " + URL);
            state main;
        }
    }
}
 
state main {
    state_entry() {
        llListen(0, "", "", "");
        llListen(-673, "", "", "");
        llSetTimerEvent(1);
    }
    timer() {
        llGetNextEmail("", "");
    }
    http_request(key id, string method, string body) {
        body = commandInterpreter(llParseString2List(body, [" "], []));
 
        if(llStringLength(body) == 0) return;
        llSay(0, body);
    }
    link_message(integer sender, integer num, string str, key id) {
        str = commandInterpreter(llParseString2List(str, [" "], []));
 
        if(llStringLength(str) == 0) return;
        llSay(0, str);
    }
    listen(integer channel, string name, key id, string message) {
        message = commandInterpreter(llParseString2List(message, [" "], []));
 
        if(llStringLength(message) == 0) return;
        //llSay(0, message);
    }
    email(string time, string address, string subject, string message, integer num_left) {
        message = commandInterpreter(llParseString2List(message, [" "], []));
 
        if(llStringLength(message) == 0) return;
        llSay(0, message);
    }
}

secondlife/primfs_and_primdrive/examples/full.txt · Last modified: 2022/11/24 07:46 by 127.0.0.1

Access website using Tor Access website using i2p Wizardry and Steamworks PGP Key


For the contact, copyright, license, warranty and privacy terms for the usage of this website please see the contact, license, privacy, copyright.