Generic Database #37

Parent #1Owner #36Flags read, fertileSource QuestCore.db

Aliases: Generic Database

19 verbs · 7 properties · 4 children

Verbs

VerbSpecFlagsDefinerLines
find find_keythis none thisrxd#3730
find_exactthis none thisrxd#3714
find_all find_all_keysthis none thisrxd#3727
_onlythis none thisrxd#3740
_everythis none thisrxd#3712
_every_keythis none thisrxd#3714
insertthis none thisrxd#3771
deletethis none thisrxd#3743
delete2this none thisrxd#3750
set_nodethis none thisrxd#371
make_nodethis none thisrxd#372
kill_nodethis none thisrxd#372
clearallthis none thisrxd#3725
clearall_bigthis none thisrxd#375
_kill_subtreesthis none thisrxd#3716
depththis none thisrxd#3714
count_entriesthis none thisrxd#3710
count_charsthis none thisrxd#3713
countany in/inside/into thisrd#3710

Properties

PropertyDefinerFlagsOwnerValue
node_perms#37rc#36"r"
data#37r#364
#37#36{"", "", {}, {}}
key#1c#36<clear>
aliases#1rc#36{"Generic Database"}
description#1rc#36"A generic `database' (well, really more like a string-indexed array if you want the truth...). See `help $generic_db' for details."
object_size#1r#36{17177, -1090650497}

Ancestry

Ancestors (nearest first): #1 Root Class

Children: #16 Registration Database, #25 Site DB, #39 Player Database, #78 Mail Name DB

Call graph

calls n37_0 #37:find n37_3 #37:_only n37_0->n37_3 n37_3->n37_3 n37_1 #37:find_exact n37_1->n37_1 n37_4 #37:_every n37_4->n37_4 n55_7 #55:remove_duplicates n37_4->n55_7 n37_5 #37:_every_key n37_5->n37_5 n37_6 #37:insert n37_6->n37_6 n42_0 #42:controls n37_6->n42_0 n37_9 #37:set_node n37_6->n37_9 n20_57 #20:common n37_6->n20_57 n37_10 #37:make_node n37_6->n37_10 n37_7 #37:delete n37_7->n42_0 n37_7->n37_9 n37_7->n37_7 n37_11 #37:kill_node n37_7->n37_11 n37_8 #37:delete2 n37_8->n42_0 n37_8->n37_9 n37_8->n37_11 n37_8->n37_8 n37_12 #37:clearall n37_12->n42_0 n37_12->n37_9 n56_8 #56:suspend_if_needed n37_12->n56_8 n37_13 #37:clearall_big n37_13->n42_0 n37_13->n37_12 n37_14 #37:_kill_subtrees n37_13->n37_14 n37_14->n42_0 n37_14->n37_11 n37_14->n37_14 n6_18 #6:tell n37_14->n6_18 n37_15 #37:depth n37_15->n6_18 n37_15->n37_15 n37_16 #37:count_entries n37_16->n6_18 n37_16->n37_16 n37_17 #37:count_chars n37_17->n6_18 n37_17->n37_17 n37_18 #37:count n37_18->n6_18 n37_18->n37_16 n37_18->n37_17

Source

find find_key

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1"find(string[,n]) => datum corresponding to string with the search starting at node \" \"+string[1..n], n defaults to 0 (root node), $ambiguous_match or $failed_match";
2"find_key(string[,n]) is like :find but returns the full string key rather than the associated datum.  Note that if several string keys present in the db share a common prefix, :find_key(prefix) will return $ambiguous_match, but if there is a unique datum associated with all of these strings :find(prefix) will return it rather than $ambiguous_match.";
3"Assumes n<=length(string)";
4{search, ?sofar = 0} = args;
5rest = search;
6prefix = search[1..sofar];
7rest[1..sofar] = "";
8info = this.(" " + prefix);
9data = (verb == "find") ? this.data | 3;
10if (i = search in info[3])
11"...exact match for one of the strings in this node...";
12return info[data][i];
13elseif (index(info[1], rest) == 1)
14"...ambiguous iff there's more than one object represented in this node..";
15return this:_only(prefix, data);
16elseif (index(rest, info[1]) != 1)
17"...search string doesn't agree with common portion...";
18return $failed_match;
19elseif (index(info[2], search[nsofar = (sofar + length(info[1])) + 1]))
20"...search string follows one of continuations leading to other nodes...";
21return this:(verb)(search, nsofar);
22else
23"...search string may partially match one of the strings in this node...";
24for i in [1..length(exacts = info[3])]
25if (index(exacts[i], search) == 1)
26return info[data][i];
27endif
28endfor
29return $failed_match;
30endif

find_exact

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1{search, ?sofar = 0} = args;
2rest = search;
3prefix = search[1..sofar];
4rest[1..sofar] = "";
5info = this.(" " + prefix);
6if (i = search in info[3])
7return info[this.data][i];
8elseif ((length(rest) <= (common = length(info[1]))) || (rest[1..common] != info[1]))
9return $failed_match;
10elseif (index(info[2], search[(sofar + common) + 1]))
11return this:find_exact(search, (sofar + common) + 1);
12else
13return $failed_match;
14endif

find_all find_all_keys

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1":find_all(string [,n=0])";
2"assumes n <= length(string)";
3{search, ?sofar = 0} = args;
4rest = search;
5prefix = search[1..sofar];
6rest[1..sofar] = "";
7info = this.(" " + prefix);
8data = (verb == "find_all") ? this.data | 3;
9if (index(info[1], rest) == 1)
10"...return entire subtree.";
11return this:((data == 3) ? "_every_key" | "_every")(prefix);
12elseif (index(rest, info[1]) != 1)
13"...common portion doesn't agree.";
14return {};
15elseif (index(info[2], rest[1 + (common = length(info[1]))]))
16"...matching strings are in a subnode.";
17return this:(verb)(search, (sofar + common) + 1);
18else
19"...matching string is in info[3].  length(rest) > common,";
20"...so there will be at most one matching string.";
21for i in [1..length(info[3])]
22if (index(info[3][i], search) == 1)
23return {info[data][i]};
24endif
25endfor
26return {};
27endif

_only

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1":_only(prefix,data) => if all strings in this node have the same datum, return it, otherwise, return $ambiguous_match.";
2if (caller != this)
3raise(E_PERM);
4endif
5{prefix, data} = args;
6info = this.(" " + prefix);
7if (data == 3)
8"... life is much simpler if there's no separate datum.";
9"... if there's more than one string here, we barf.";
10if (info[2] || (length(info[3]) > 1))
11return $ambiguous_match;
12elseif (info[3])
13return info[3][1];
14else
15"..this can only happen with the root node of an empty db.";
16return $failed_match;
17endif
18elseif (info[2])
19what = this:_only(tostr(prefix, info[1], info[2][1]), data);
20if (what == $ambiguous_match)
21return what;
22endif
23elseif (info[data])
24what = info[data][1];
25info[data] = listdelete(info[data], 1);
26else
27"..this can only happen with the root node of an empty db.";
28return $failed_match;
29endif
30for x in (info[data])
31if (what != x)
32return $ambiguous_match;
33endif
34endfor
35for i in [2..length(info[2])]
36if (what != this:_only(tostr(prefix, info[1], info[2][i]), data))
37return $ambiguous_match;
38endif
39endfor
40return what;

_every

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1if (caller != this)
2raise(E_PERM);
3endif
4info = this.(" " + args[1]);
5prefix = args[1] + info[1];
6r = $list_utils:remove_duplicates(info[4]);
7for i in [1..length(branches = info[2])]
8for new in (this:_every(prefix + branches[i]))
9r = setadd(r, new);
10endfor
11endfor
12return r;

_every_key

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1if (caller != this)
2raise(E_PERM);
3endif
4info = this.(" " + args[1]);
5prefix = args[1] + info[1];
6r = info[3];
7for i in [1..length(branches = info[2])]
8for new in (this:_every_key(prefix + branches[i]))
9r = setadd(r, new);
10(ticks_left() < 3600) && suspend(0);
11endfor
12(seconds_left() < 4) && suspend(0);
13endfor
14return r;

insert

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1":insert([n,]string,datum) -- inserts <string,datum> correspondence into tree starting at node \" \"+string[1..n], n defaulting to 0 (root node).";
2"Assumes length(string) >= n";
3"Returns {old_datum} (or 1) if there was a <string,old_datum> correspondence there before, otherwise returns 0";
4if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
5return E_PERM;
6endif
7has_datum = this.data > 3;
8if (typeof(sofar = args[1]) == INT)
9search = args[2];
10datum = has_datum ? args[3] | 0;
11else
12search = sofar;
13sofar = 0;
14datum = has_datum ? args[2] | 0;
15endif
16prefix = search[1..sofar];
17info = this.(" " + prefix);
18if (i = search in info[3])
19"... exact match ...";
20if (has_datum)
21previous = {info[this.data][i]};
22info[this.data][i] = datum;
23this:set_node(prefix, @info);
24return previous;
25else
26return 1;
27endif
28endif
29rest = search;
30rest[1..sofar] = "";
31if (index(rest, info[1]) != 1)
32"... find where new string disagrees with common portion...";
33c = $string_utils:common(rest, info[1]) + 1;
34"... make a new node with a shorter common portion....";
35this:make_node(prefix + info[1][1..c], @listset(info, info[1][c + 1..$], 1));
36this:set_node(prefix, info[1][1..c - 1], info[1][c], {search}, @has_datum ? {{datum}} | {});
37return 0;
38elseif (rest == info[1])
39".. new string == common portion, insert...";
40info[3] = {@info[3], search};
41if (has_datum)
42info[this.data] = {@info[this.data], datum};
43endif
44this:set_node(prefix, @info);
45return 0;
46elseif (index(info[2], search[nsofar = (sofar + length(info[1])) + 1]))
47"... new string matches pre-existing continuation. insert in subnode....";
48return this:insert(nsofar, search, datum);
49else
50"... new string may blow away one of the exact matches (i.e., matches one of them up to the first character beyond the common portion) in which case we need to create a new subnode....";
51s = search[1..nsofar];
52for m in (info[3])
53if (index(m, s) == 1)
54i = m in info[3];
55"... we know m != search ...";
56"... string m has been blown away.  create new node ...";
57cbegin = cafter = length(s) + 1;
58cend = $string_utils:common(search, m);
59this:make_node(s, m[cbegin..cend], "", {search, m}, @has_datum ? {{datum, info[this.data][i]}} | {});
60this:set_node(prefix, info[1], info[2] + s[nsofar], listdelete(info[3], i), @has_datum ? {listdelete(info[this.data], i)} | {});
61return 0;
62endif
63endfor
64"... new string hasn't blown away any of the exact matches, insert it as a new exact match...";
65info[3] = {search, @info[3]};
66if (has_datum)
67info[this.data] = {datum, @info[this.data]};
68endif
69this:set_node(prefix, @info);
70return 0;
71endif

delete

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1":delete(string[,n]) deletes any <string,something> pair from the tree starting at node \" \"+string[1..n], n defaulting to 0 (root node)";
2"Returns {something} if such a pair existed, otherwise returns 0";
3"If that node is not the root node and ends up containing only one string and no subnodes, we kill it and return {something,string2,something2} where <string2,something2> is the remaining pair.";
4if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
5return E_PERM;
6endif
7{search, ?sofar = 0} = args;
8rest = search;
9prefix = search[1..sofar];
10rest[1..sofar] = "";
11info = this.(" " + prefix);
12if (i = search in info[3])
13previous = {info[this.data][i]};
14info[3] = listdelete(info[3], i);
15if (this.data > 3)
16info[this.data] = listdelete(info[this.data], i);
17endif
18elseif ((rest == info[1]) || ((index(rest, info[1]) != 1) || (!index(info[2], search[d = (sofar + length(info[1])) + 1]))))
19"... hmm string isn't in here...";
20return 0;
21elseif ((previous = this:delete(search, d)) && (length(previous) > 1))
22i = index(info[2], search[d]);
23info[2][i..i] = "";
24info[3] = {previous[2], @info[3]};
25if (this.data > 3)
26info[this.data] = {previous[3], @info[this.data]};
27endif
28previous = previous[1..1];
29else
30return previous;
31endif
32if ((!prefix) || ((length(info[3]) + length(info[2])) != 1))
33this:set_node(prefix, @info);
34return previous;
35elseif (info[3])
36this:kill_node(prefix);
37return {@previous, info[3][1], info[this.data][1]};
38else
39sub = this.(" " + (p = tostr(prefix, info[1], info[2])));
40this:kill_node(p);
41this:set_node(prefix, @listset(sub, tostr(info[1], info[2], sub[1]), 1));
42return previous;
43endif

delete2

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1":delete2(string,datum[,n]) deletes the pair <string,datum> from the tree starting at node \" \"+string[1..n], n defaulting to 0 (root node)";
2"Similar to :delete except that if the entry for that string has a different associated datum, it will not be removed.  ";
3":delete2(string,datum) is equivalent to ";
4" ";
5"  if(this:find_exact(string)==datum) ";
6"    this:delete(string); ";
7"  endif";
8if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
9return E_PERM;
10endif
11{search, datum, ?sofar = 0} = args;
12rest = search;
13prefix = search[1..sofar];
14rest[1..sofar] = "";
15info = this.(" " + prefix);
16if (i = search in info[3])
17previous = {info[this.data][i]};
18if (previous[1] != datum)
19return previous;
20endif
21info[3] = listdelete(info[3], i);
22if (this.data > 3)
23info[this.data] = listdelete(info[this.data], i);
24endif
25elseif ((rest == info[1]) || ((index(rest, info[1]) != 1) || (!index(info[2], search[d = (sofar + length(info[1])) + 1]))))
26"... hmm string isn't in here...";
27return 0;
28elseif ((previous = this:delete2(search, datum, d)) && (length(previous) > 1))
29i = index(info[2], search[d]);
30info[2][i..i] = "";
31info[3] = {previous[2], @info[3]};
32if (this.data > 3)
33info[this.data] = {previous[3], @info[this.data]};
34endif
35previous = previous[1..1];
36else
37return previous;
38endif
39if ((!prefix) || ((length(info[3]) + length(info[2])) != 1))
40this:set_node(prefix, @info);
41return previous;
42elseif (info[3])
43this:kill_node(prefix);
44return {@previous, info[3][1], info[this.data][1]};
45else
46sub = this.(" " + (p = tostr(prefix, info[1], info[2])));
47this:kill_node(p);
48this:set_node(prefix, @listset(sub, tostr(info[1], info[2], sub[1]), 1));
49return previous;
50endif

set_node

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1return (caller != this) ? E_PERM | (this.(" " + args[1]) = listdelete(args, 1));

make_node

Spec this none thisFlags rxdOwner #2Definer #37

Referenced by

Source

1"WIZARDLY";
2return (caller != this) ? E_PERM | add_property(this, " " + args[1], listdelete(args, 1), {$generic_db.owner, this.node_perms});

kill_node

Spec this none thisFlags rxdOwner #2Definer #37

Referenced by

Source

1"WIZARDLY";
2return (caller != this) ? E_PERM | delete_property(this, " " + args[1]);

clearall

Spec this none thisFlags rxdOwner #2Definer #37

Referenced by

Source

1"WIZARDLY";
2if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
3return E_PERM;
4endif
5if (args && ((d = args[1]) in {3, 4}))
6this.data = d;
7endif
8root = {"", "", {}, @(this.data > 3) ? {{}} | {}};
9"...since the for loop contains a suspend, we want to keep people";
10"...from getting at properties which are now garbage but which we";
11"...haven't had a chance to wipe yet.  Somebody might yet succeed";
12"...in adding something; thus we have the outer while loop.";
13this:set_node("", 37);
14while (this.(" ") != root)
15this:set_node("", @root);
16for p in (properties(this))
17if ((p[1] == " ") && (p != " "))
18delete_property(this, p);
19endif
20"...Bleah; db is inconsistent now....";
21"...At worst someone will add something that references an";
22"...existing property.  He will deserve to die...";
23$command_utils:suspend_if_needed(0);
24endfor
25endwhile

clearall_big

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

none

Source

1if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
2return E_PERM;
3endif
4this:_kill_subtrees("", 0);
5this:clearall(@args);

_kill_subtrees

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1":_kill_subtree(node,count)...wipes out all subtrees";
2"...returns count + number of nodes removed...";
3if (!($perm_utils:controls(caller_perms(), this) || (caller == this)))
4return E_PERM;
5endif
6info = this.(" " + (prefix = args[1]));
7count = args[2];
8if ((ticks_left() < 500) || (seconds_left() < 2))
9player:tell("...", count);
10suspend(0);
11endif
12for i in [1..length(info[2])]
13count = this:_kill_subtrees(n = tostr(prefix, info[1], info[2][i]), count) + 1;
14this:kill_node(n);
15endfor
16return count;

depth

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1info = this.(" " + (prefix = (args || {""})[1]));
2depth = 0;
3string = prefix;
4if ((ticks_left() < 500) || (seconds_left() < 2))
5player:tell("...", prefix);
6suspend(0);
7endif
8for i in [1..length(info[2])]
9if ((r = this:depth(tostr(prefix, info[1], info[2][i])))[1] > depth)
10depth = r[1];
11string = r[2];
12endif
13endfor
14return {depth + 1, string};

count_entries

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1info = this.(" " + (prefix = args[1]));
2count = length(info[3]) + args[2];
3if ((ticks_left() < 500) || (seconds_left() < 2))
4player:tell("...", count);
5suspend(0);
6endif
7for i in [1..length(info[2])]
8count = this:count_entries(tostr(prefix, info[1], info[2][i]), count);
9endfor
10return count;

count_chars

Spec this none thisFlags rxdOwner #36Definer #37

Referenced by

Source

1info = this.(" " + (prefix = args[1]));
2count = args[2];
3for s in (info[3])
4count = count + length(s);
5endfor
6if ((ticks_left() < 500) || (seconds_left() < 2))
7player:tell("...", count);
8suspend(0);
9endif
10for i in [1..length(info[2])]
11count = this:count_chars(tostr(prefix, info[1], info[2][i]), count);
12endfor
13return count;

count

Spec any in/inside/into thisFlags rdOwner #36Definer #37

Referenced by

none

Source

1"count [entries|chars] in <db>";
2"  reports on the number of distinct string keys or the number of characters";
3"  in all string keys in the db";
4if (index("entries", dobjstr) == 1)
5player:tell(this:count_entries("", 0), " strings in ", this.name, "(", this, ")");
6elseif (index("chars", dobjstr) == 1)
7player:tell(this:count_chars("", 0), " chars in ", this.name, "(", this, ")");
8else
9player:tell("Usage: ", verb, " entries|chars in <db>");
10endif