Generic Database Tree #30

Parent #1Owner #29Flags read, fertileSource hellcore/hellcore.db

Aliases: Generic Database Tree, dbtree

19 verbs · 13 properties · 2 children

Verbs

VerbSpecFlagsDefinerLines
find find_keythis none thisrxd#3030
find_exactthis none thisrxd#3014
find_all find_all_keysthis none thisrxd#3027
_onlythis none thisrxd#3040
_everythis none thisrxd#3012
_every_keythis none thisrxd#3014
insertthis none thisrxd#3071
deletethis none thisrxd#3043
delete2this none thisrxd#3050
set_nodethis none thisrxd#301
make_nodethis none thisrxd#302
kill_nodethis none thisrxd#302
clearallthis none thisrxd#3025
clearall_bigthis none thisrxd#305
_kill_subtreesthis none thisrxd#3016
depththis none thisrxd#3014
count_entriesthis none thisrxd#3010
count_charsthis none thisrxd#3013
countany in/inside/into thisrxd#3010

Properties

PropertyDefinerFlagsOwnerValue
node_perms#30rc#29"r"
data#30r#294
#30#29{"", "", {}, {}}
aliases#1rc#29{"Generic Database Tree", "dbtree"}
description#1rc#29"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#29{16376, 1298433815}
hidden_verbs#1rc#29<clear>
phelp_msg#1rc#29<clear>
weight#1rc#29<clear>
owner_verbs#1rc#29<clear>
plural_name#1rc#29<clear>
client_image#1rc#29<clear>
listening#1rc#29<clear>

Ancestry

Ancestors (nearest first): #1 root

Children: #11 Registration Database, #32 Player Database

Call graph

calls n30_0 #30:find n30_3 #30:_only n30_0->n30_3 n30_3->n30_3 n30_1 #30:find_exact n30_1->n30_1 n30_4 #30:_every n30_4->n30_4 n47_7 #47:remove_duplicates n30_4->n47_7 n30_5 #30:_every_key n30_5->n30_5 n48_8 #48:suspend_if_needed n30_5->n48_8 n30_6 #30:insert n30_6->n30_6 n42_0 #42:controls n30_6->n42_0 n30_9 #30:set_node n30_6->n30_9 n20_57 #20:common n30_6->n20_57 n30_10 #30:make_node n30_6->n30_10 n30_7 #30:delete n30_7->n42_0 n30_7->n30_9 n30_7->n30_7 n30_11 #30:kill_node n30_7->n30_11 n30_8 #30:delete2 n30_8->n42_0 n30_8->n30_9 n30_8->n30_11 n30_8->n30_8 n30_12 #30:clearall n30_12->n48_8 n30_12->n42_0 n30_12->n30_9 n30_13 #30:clearall_big n30_13->n42_0 n30_13->n30_12 n30_14 #30:_kill_subtrees n30_13->n30_14 n30_14->n42_0 n30_14->n30_11 n30_14->n30_14 n107_43 #107:tell n30_14->n107_43 n30_15 #30:depth n30_15->n107_43 n30_15->n30_15 n30_16 #30:count_entries n30_16->n107_43 n30_16->n30_16 n30_17 #30:count_chars n30_17->n107_43 n30_17->n30_17 n30_18 #30:count n30_18->n107_43 n30_18->n30_16 n30_18->n30_17

Source

find find_key

Spec this none thisFlags rxdOwner #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

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$command_utils:suspend_if_needed(0);
11endfor
12$command_utils:suspend_if_needed(0);
13endfor
14return r;

insert

Spec this none thisFlags rxdOwner #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

Referenced by

Source

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

make_node

Spec this none thisFlags rxdOwner #361Definer #30

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 #361Definer #30

Referenced by

Source

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

clearall

Spec this none thisFlags rxdOwner #361Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 #29Definer #30

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 rxdOwner #29Definer #30

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