cfad47cfa3/tads3/vmdict.cpp
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 59.8 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
Showing without highlighting since it looks like a big file and may slow your browser - show with highlighting
Show/hide line numbers#ifdef RCSID
static char RCSid[] =
"$Header$";
#endif
/*
* Copyright (c) 2000, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
vmdict.cpp - dictionary metaclass
Function
Notes
Modified
01/24/00 MJRoberts - Creation
*/
#include <stdlib.h>
#include <string.h>
#include "t3std.h"
#include "vmdict.h"
#include "vmerr.h"
#include "vmerrnum.h"
#include "vmtype.h"
#include "vmglob.h"
#include "vmobj.h"
#include "vmundo.h"
#include "vmmcreg.h"
#include "vmfile.h"
#include "vmhash.h"
#include "vmbif.h"
#include "vmmeta.h"
#include "vmstack.h"
#include "vmstr.h"
#include "vmlst.h"
#include "vmrun.h"
#include "vmstrcmp.h"
#include "vmpredef.h"
/* ------------------------------------------------------------------------ */
/*
* Dictionary undo record. Each time we change the dictionary, we track
* the change with one of these records - we attach this record to the
* standard undo record via the 'ptrval' field.
*/
struct dict_undo_rec
{
/* action type */
enum dict_undo_action action;
/* object value stored in dictionary record */
vm_obj_id_t obj;
/* vocabulary property for word association */
vm_prop_id_t prop;
/* length of the word */
size_t len;
/* text of the word */
char txt[1];
};
/* ------------------------------------------------------------------------ */
/*
* Generic comparator-based hash
*/
class CVmHashFuncComparator: public CVmHashFunc
{
public:
CVmHashFuncComparator(VMG_ vm_obj_id_t obj)
{
globals_ = VMGLOB_ADDR;
comparator_ = obj;
}
/* compute the hash value */
unsigned int compute_hash(const char *str, size_t len) const
{
vm_val_t val;
vm_val_t cobj_val;
VMGLOB_PTR(globals_);
/* create a string object to represent the argument, and push it */
G_stk->push()->set_obj(CVmObjString::create(vmg_ FALSE, str, len));
/* invoke the calcHash method */
cobj_val.set_obj(comparator_);
G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->calc_hash_prop,
&cobj_val, 1);
/* retrieve the result from R0 */
val = *G_interpreter->get_r0();
/* we need an integer value from the method */
if (val.typ == VM_INT)
{
/* return the hash value from the comparator */
return val.val.intval;
}
else
{
/* no or invalid hash value - throw an error */
err_throw(VMERR_BAD_TYPE_BIF);
AFTER_ERR_THROW(return FALSE;)
}
}
private:
/* my comparator object */
vm_obj_id_t comparator_;
/* system globals */
vm_globals *globals_;
};
/*
* StringComparator-based hash
*/
class CVmHashFuncStrComp: public CVmHashFunc
{
public:
CVmHashFuncStrComp(CVmObjStrComp *comp) { comparator_ = comp; }
/* compute the hash value */
unsigned int compute_hash(const char *str, size_t len) const
{
/* ask the StringComparator to do the work */
return comparator_->calc_str_hash(str, len);
}
private:
/* my StringComparator object */
CVmObjStrComp *comparator_;
};
/* ------------------------------------------------------------------------ */
/*
* dictionary object statics
*/
/* metaclass registration object */
static CVmMetaclassDict metaclass_reg_obj;
CVmMetaclass *CVmObjDict::metaclass_reg_ = &metaclass_reg_obj;
/* function table */
int (CVmObjDict::
*CVmObjDict::func_table_[])(VMG_ vm_obj_id_t self,
vm_val_t *retval, uint *argc) =
{
&CVmObjDict::getp_undef,
&CVmObjDict::getp_set_comparator,
&CVmObjDict::getp_find,
&CVmObjDict::getp_add,
&CVmObjDict::getp_del,
&CVmObjDict::getp_is_defined,
&CVmObjDict::getp_for_each_word
};
/* ------------------------------------------------------------------------ */
/*
* Dictionary object implementation
*/
/*
* create dynamically using stack arguments
*/
vm_obj_id_t CVmObjDict::create_from_stack(VMG_ const uchar **pc_ptr,
uint argc)
{
vm_obj_id_t id;
vm_obj_id_t comp;
CVmObjDict *obj;
/* check arguments */
if (argc != 0 && argc != 1)
err_throw(VMERR_WRONG_NUM_OF_ARGS);
/* get the Comparator, but leave it on the stack as gc protection */
if (argc == 0 || G_stk->get(0)->typ == VM_NIL)
comp = VM_INVALID_OBJ;
else if (G_stk->get(0)->typ == VM_OBJ)
comp = G_stk->get(0)->val.obj;
else
err_throw(VMERR_BAD_TYPE_BIF);
/*
* allocate the object ID - this type of construction never creates
* a root object
*/
id = vm_new_id(vmg_ FALSE, TRUE, TRUE);
/* create the object */
obj = new (vmg_ id) CVmObjDict(vmg0_);
/* set the comparator */
obj->set_comparator(vmg_ comp);
/* build an empty initial hash table */
obj->create_hash_table(vmg0_);
/*
* mark the object as modified since image load, since it doesn't
* come from the image file at all
*/
obj->get_ext()->modified_ = TRUE;
/* discard our gc protection */
G_stk->discard();
/* return the new ID */
return id;
}
/* ------------------------------------------------------------------------ */
/*
* constructor
*/
CVmObjDict::CVmObjDict(VMG0_)
{
/* allocate our extension structure from the variable heap */
ext_ = (char *)G_mem->get_var_heap()
->alloc_mem(sizeof(vm_dict_ext), this);
/* we have no image data yet */
get_ext()->image_data_ = 0;
get_ext()->image_data_size_ = 0;
/* no hash table yet */
get_ext()->hashtab_ = 0;
/* no non-image entries yet */
get_ext()->modified_ = FALSE;
/* no comparator yet */
get_ext()->comparator_ = VM_INVALID_OBJ;
set_comparator_type(vmg_ VM_INVALID_OBJ);
}
/* ------------------------------------------------------------------------ */
/*
* notify of deletion
*/
void CVmObjDict::notify_delete(VMG_ int /*in_root_set*/)
{
/* free our additional data */
if (ext_ != 0)
{
/* free our hash table */
if (get_ext()->hashtab_ != 0)
delete get_ext()->hashtab_;
/* free the extension */
G_mem->get_var_heap()->free_mem(ext_);
}
}
/* ------------------------------------------------------------------------ */
/*
* Set the comparator object
*/
void CVmObjDict::set_comparator(VMG_ vm_obj_id_t obj)
{
/* if this is the same as the current comparator, there's nothing to do */
if (obj == get_ext()->comparator_)
return;
/* remember the new comparator */
get_ext()->comparator_ = obj;
/* figure out what kind of compare functions to use */
set_comparator_type(vmg_ obj);
/* rebuild the hash tale */
if (get_ext()->hashtab_ != 0)
create_hash_table(vmg0_);
}
/*
* Set the string-compare function based on the given comparator.
*/
void CVmObjDict::set_comparator_type(VMG_ vm_obj_id_t obj)
{
/* check what kind of object we have */
if (obj == VM_INVALID_OBJ)
{
/* there's no comparator at all, so simply use an exact comparison */
get_ext()->comparator_type_ = VMDICT_COMP_NONE;
}
else if (CVmObjStrComp::is_strcmp_obj(vmg_ obj))
{
/* it's a StringComparator */
get_ext()->comparator_type_ = VMDICT_COMP_STRCOMP;
}
else
{
/* it's a generic comparator object */
get_ext()->comparator_type_ = VMDICT_COMP_GENERIC;
}
/* remember the object pointer, if there is one */
get_ext()->comparator_obj_ = (obj == VM_INVALID_OBJ
? 0 : vm_objp(vmg_ obj));
}
/* ------------------------------------------------------------------------ */
/*
* Rebuild the hash table. If there's an existing hash table, we'll move
* the entries from the old table to the new table, and delete the old
* table.
*/
void CVmObjDict::create_hash_table(VMG0_)
{
CVmHashTable *new_tab;
CVmHashFunc *hash_func;
/*
* Create our hash function. If we have a comparator object, base the
* hash function on the comparator; use a special hash function if we
* specifically have a StringComparator, since we can call these
* directly for better efficiency. If we have no comparator, create a
* generic exact string match hash function.
*/
if (get_ext()->comparator_ != VM_INVALID_OBJ)
{
/*
* use our special StringComparator hash function if possible;
* otherwise, use a generic comparator hash function
*/
if (CVmObjStrComp::is_strcmp_obj(vmg_ get_ext()->comparator_))
{
/* create a StringComparator hash function */
hash_func = new CVmHashFuncStrComp(
(CVmObjStrComp *)vm_objp(vmg_ get_ext()->comparator_));
}
else
{
/* create a generic comparator hash function */
hash_func = new CVmHashFuncComparator(
vmg_ get_ext()->comparator_);
}
}
else
{
/* create a simple exact-match hash */
hash_func = new CVmHashFuncCS();
}
/* create the hash table */
new_tab = new CVmHashTable(256, hash_func, TRUE);
/* if we had a previous hash table, move its contents to the new table */
if (get_ext()->hashtab_ != 0)
{
/* copy the old hash table to the new one */
get_ext()->hashtab_->move_entries_to(new_tab);
/* delete the old hash table */
delete get_ext()->hashtab_;
}
/* store the new hash table in the extension */
get_ext()->hashtab_ = new_tab;
}
/* ------------------------------------------------------------------------ */
/*
* Check for a match between two strings, using the current comparator.
* Returns true if the strings match, false if not; fills in *result_val
* with the actual result value from the comparator's matchValues()
* function.
*/
int CVmObjDict::match_strings(VMG_ const vm_val_t *valstrval,
const char *valstr, size_t vallen,
const char *refstr, size_t reflen,
vm_val_t *result_val)
{
vm_val_t cobj_val;
/* check what kind of comparator we have */
switch(get_ext()->comparator_type_)
{
case VMDICT_COMP_NONE:
/* no comparator - compare the strings byte-for-byte */
if (vallen == reflen && memcmp(valstr, refstr, vallen) == 0)
{
/* match - return true */
result_val->set_int(1);
return TRUE;
}
else
{
/* no match - return false */
result_val->set_int(0);
return FALSE;
}
case VMDICT_COMP_STRCOMP:
/* match the value directly with the StringComparator */
result_val->set_int(
((CVmObjStrComp *)get_ext()->comparator_obj_)->match_strings(
valstr, vallen, refstr, reflen));
/* we matched if the result was non-zero */
return result_val->val.intval;
case VMDICT_COMP_GENERIC:
/* 2nd param: push the reference string, as a string object */
G_stk->push()->set_obj(
CVmObjString::create(vmg_ FALSE, refstr, reflen));
/* 1st param: push the value string */
if (valstrval != 0)
{
/* push the value string as given */
G_stk->push(valstrval);
}
else
{
/* no value string was given - create one and push it */
G_stk->push()->set_obj(
CVmObjString::create(vmg_ FALSE, valstr, vallen));
}
/* call the comparator's matchValues method */
cobj_val.set_obj(get_ext()->comparator_);
G_interpreter->get_prop(vmg_ 0, &cobj_val,
G_predef->match_values_prop, &cobj_val, 2);
/* get the result from R0 */
*result_val = *G_interpreter->get_r0();
/* we matched if the result isn't 0 or nil */
return !(result_val->typ == VM_NIL
|| (result_val->typ == VM_INT
&& result_val->val.intval == 0));
}
/* we should never get here, but just in case... */
return FALSE;
}
/* ------------------------------------------------------------------------ */
/*
* Calculate a hash value with the current comparator
*/
unsigned int CVmObjDict::calc_str_hash(VMG_ const vm_val_t *valstrval,
const char *valstr, size_t vallen)
{
vm_val_t result;
vm_val_t cobj_val;
/* check what kind of comparator we have */
switch(get_ext()->comparator_type_)
{
case VMDICT_COMP_NONE:
/* no comparator - use the hash table's basic hash calculation */
return get_ext()->hashtab_->compute_hash(valstr, vallen);
case VMDICT_COMP_STRCOMP:
/* calculate the hash directly with the StringComparator */
return ((CVmObjStrComp *)get_ext()->comparator_obj_)->calc_str_hash(
valstr, vallen);
case VMDICT_COMP_GENERIC:
/* push the value string */
if (valstrval != 0)
{
/* push the value string as given */
G_stk->push(valstrval);
}
else
{
/* no value string was given - create one and push it */
G_stk->push()->set_obj(
CVmObjString::create(vmg_ FALSE, valstr, vallen));
}
/* call the comparator object's calcHash method */
cobj_val.set_obj(get_ext()->comparator_);
G_interpreter->get_prop(vmg_ 0, &cobj_val, G_predef->calc_hash_prop,
&cobj_val, 1);
/* get the result from R0 */
result = *G_interpreter->get_r0();
/* make sure it's an integer */
if (result.typ != VM_INT)
err_throw(VMERR_BAD_TYPE_BIF);
/* return the result */
return (unsigned int)result.val.intval;
}
/* we should never get here, but just in case... */
return 0;
}
/* ------------------------------------------------------------------------ */
/*
* Set a property. We have no settable properties, so simply signal an
* error indicating that the set-prop call is invalid.
*/
void CVmObjDict::set_prop(VMG_ CVmUndo *, vm_obj_id_t,
vm_prop_id_t, const vm_val_t *)
{
/* no settable properties - throw an error */
err_throw(VMERR_INVALID_SETPROP);
}
/* ------------------------------------------------------------------------ */
/*
* get a property
*/
int CVmObjDict::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
vm_obj_id_t self, vm_obj_id_t *source_obj,
uint *argc)
{
uint func_idx;
/* translate the property into a function vector index */
func_idx = G_meta_table
->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
/* call the appropriate function */
if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
{
*source_obj = metaclass_reg_->get_class_obj(vmg0_);
return TRUE;
}
/* inherit default handling from the base object class */
return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc);
}
/* ------------------------------------------------------------------------ */
/*
* evaluate property: set the comparator
*/
int CVmObjDict::getp_set_comparator(VMG_ vm_obj_id_t self, vm_val_t *val,
uint *argc)
{
static CVmNativeCodeDesc desc(1);
vm_obj_id_t comp;
/* check arguments */
if (get_prop_check_argc(val, argc, &desc))
return TRUE;
/* get the comparator object */
switch(G_stk->get(0)->typ)
{
case VM_NIL:
comp = VM_INVALID_OBJ;
break;
case VM_OBJ:
comp = G_stk->get(0)->val.obj;
break;
default:
err_throw(VMERR_BAD_TYPE_BIF);
}
/* if there's a global undo object, add undo for the change */
if (G_undo != 0)
{
dict_undo_rec *undo_rec;
/* create an undo record with the original comparator */
undo_rec = alloc_undo_rec(DICT_UNDO_COMPARATOR, 0, 0);
undo_rec->obj = get_ext()->comparator_;
/* add the undo record */
add_undo_rec(vmg_ self, undo_rec);
}
/* set our new comparator object */
set_comparator(vmg_ comp);
/* mark the object as modified since load */
get_ext()->modified_ = TRUE;
/* discard arguments */
G_stk->discard();
/* no return value - just return nil */
val->set_nil();
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* result entry for find_ctx
*/
struct find_entry
{
/* matching object */
vm_obj_id_t obj;
/* match result code */
vm_val_t match_result;
};
/*
* page of results for find_ctx
*/
const int FIND_ENTRIES_PER_PAGE = 32;
struct find_entry_page
{
/* next in list of pages */
find_entry_page *nxt;
/* array of entries */
find_entry entry[FIND_ENTRIES_PER_PAGE];
};
/*
* enumeration callback context for getp_find
*/
struct find_ctx
{
/* set up a context that adds results to our internal list */
find_ctx(VMG_ CVmObjDict *dict_obj, const vm_val_t *val,
const char *str, size_t len, vm_prop_id_t prop)
{
/* remember the dictionary object we're searching */
dict = dict_obj;
/* remember the globals */
globals = VMGLOB_ADDR;
/* keep a pointer to the search string value */
this->strval = val;
this->strp = str;
this->strl = len;
/* remember the property */
voc_prop = prop;
/* no results yet */
results_head = 0;
results_tail = 0;
result_cnt = 0;
/*
* set the free pointer to the end of the non-existent current
* page, so we know we have to allocate a new page before adding
* the first entry
*/
result_free = FIND_ENTRIES_PER_PAGE;
}
~find_ctx()
{
/* free our list of results pages */
while (results_head != 0)
{
/* remember the current page */
find_entry_page *cur = results_head;
/* move on to the next page */
results_head = results_head->nxt;
/* delete the current page */
t3free(cur);
}
}
/* add a result, expanding the result array if necessary */
void add_result(vm_obj_id_t obj, const vm_val_t *match_result)
{
/*
* if we have no pages, or we're out of room on the current page,
* allocate a new page
*/
if (result_free == FIND_ENTRIES_PER_PAGE)
{
find_entry_page *pg;
/* allocate a new page */
pg = (find_entry_page *)t3malloc(sizeof(find_entry_page));
/* link it in at the end of the list */
pg->nxt = 0;
if (results_tail != 0)
results_tail->nxt = pg;
else
results_head = pg;
results_tail = pg;
/* reset the free pointer to the start of the new page */
result_free = 0;
}
/* add this result */
results_tail->entry[result_free].obj = obj;
results_tail->entry[result_free].match_result = *match_result;
++result_free;
++result_cnt;
}
/* make our results into a list */
vm_obj_id_t results_to_list(VMG0_)
{
vm_obj_id_t lst_id;
CVmObjList *lst;
find_entry_page *pg;
int idx;
/*
* Allocate a list of the appropriate size. We need two list
* entries per result, since we need to store the object and the
* match result code for each result.
*/
lst_id = CVmObjList::create(vmg_ FALSE, result_cnt * 2);
lst = (CVmObjList *)vm_objp(vmg_ lst_id);
/* add all entries */
for (idx = 0, pg = results_head ; idx < result_cnt ; pg = pg->nxt)
{
find_entry *ep;
int pg_idx;
/* add each entry on this page */
for (ep = pg->entry, pg_idx = 0 ;
idx < result_cnt && pg_idx < FIND_ENTRIES_PER_PAGE ;
++idx, ++pg_idx, ++ep)
{
vm_val_t ele;
/* add this entry to the list */
ele.set_obj(ep->obj);
lst->cons_set_element(idx*2, &ele);
lst->cons_set_element(idx*2 + 1, &ep->match_result);
}
}
/* return the list */
return lst_id;
}
/* head/tail of list of result pages */
find_entry_page *results_head;
find_entry_page *results_tail;
/* next available entry on current results page */
int result_free;
/* total number of result */
int result_cnt;
/* the string to find */
const vm_val_t *strval;
const char *strp;
size_t strl;
/* vocabulary property value to match */
vm_prop_id_t voc_prop;
/* VM globals */
vm_globals *globals;
/* dictionary object we're searching */
CVmObjDict *dict;
};
/*
* Callback for getp_find hash match enumeration
*/
void CVmObjDict::find_cb(void *ctx0, CVmHashEntry *entry0)
{
find_ctx *ctx = (find_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_val_t match_val;
VMGLOB_PTR(ctx->globals);
/* if this entry matches the search string, process it */
if (ctx->dict->match_strings(vmg_ ctx->strval, ctx->strp, ctx->strl,
entry->getstr(), entry->getlen(),
&match_val))
{
vm_dict_entry *cur;
/* process the items under this entry */
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/*
* If it matches the target property, it's a hit. If we're
* searching for any property at all (indicated by the
* context property being set to VM_INVALID_PROP), count
* everything.
*/
if (cur->prop_ == ctx->voc_prop
|| ctx->voc_prop == VM_INVALID_PROP)
{
/* process the result */
ctx->add_result(cur->obj_, &match_val);
}
}
}
}
/* ------------------------------------------------------------------------ */
/*
* property evaluation - look up a word
*/
int CVmObjDict::getp_find(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc)
{
static CVmNativeCodeDesc desc(1, 1);
uint orig_argc = (argc != 0 ? *argc : 0);
vm_val_t *arg0;
vm_val_t *arg1;
const char *strp;
size_t strl;
vm_prop_id_t voc_prop;
unsigned int hash;
/* check arguments */
if (get_prop_check_argc(val, argc, &desc))
return TRUE;
/*
* make sure the first argument is a string, but leave it on the stack
* for gc protection
*/
arg0 = G_stk->get(0);
if ((strp = arg0->get_as_string(vmg0_)) == 0)
err_throw(VMERR_STRING_VAL_REQD);
/* scan and skip the string's length prefix */
strl = vmb_get_len(strp);
strp += VMB_LEN;
/*
* get the property ID, which can be omitted or specified as nil to
* indicate any property
*/
if (orig_argc < 2)
voc_prop = VM_INVALID_PROP;
else if ((arg1 = G_stk->get(1))->typ == VM_NIL)
voc_prop = VM_INVALID_PROP;
else if (arg1->typ == VM_PROP)
voc_prop = arg1->val.prop;
else
err_throw(VMERR_PROPPTR_VAL_REQD);
/* calculate the hash value */
hash = calc_str_hash(vmg_ arg0, strp, strl);
/* enumerate everything that matches the string's hash code */
find_ctx ctx(vmg_ this, arg0, strp, strl, voc_prop);
get_ext()->hashtab_->enum_hash_matches(hash, &find_cb, &ctx);
/* build a list out of the results, and return the list */
val->set_obj(ctx.results_to_list(vmg0_));
/* discard arguments */
G_stk->discard(orig_argc);
/* success */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluation - add a word
*/
int CVmObjDict::getp_add(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc)
{
vm_obj_id_t obj;
vm_val_t str_val;
vm_prop_id_t voc_prop;
CVmObject *objp;
const char *lst;
const char *str;
static CVmNativeCodeDesc desc(3);
/* check arguments */
if (get_prop_check_argc(val, argc, &desc))
return TRUE;
/* pop the object, word string, and property ID arguments */
obj = CVmBif::pop_obj_val(vmg0_);
G_stk->pop(&str_val);
voc_prop = CVmBif::pop_propid_val(vmg0_);
/* ensure that the object value isn't a string or list */
objp = vm_objp(vmg_ obj);
if (objp->get_as_list() != 0 || objp->get_as_string(vmg0_) != 0)
err_throw(VMERR_OBJ_VAL_REQD);
/* if the string argument is a list, add each word from the list */
if ((lst = str_val.get_as_list(vmg0_)) != 0)
{
size_t cnt;
size_t idx;
vm_val_t ele_val;
/* get and skip the list count prefix */
cnt = vmb_get_len(lst);
/* iterate over the list, using 1-based indices */
for (idx = 1 ; idx <= cnt ; ++idx)
{
/* get the next value from the list */
CVmObjList::index_list(vmg_ &ele_val, lst, idx);
/* get the next string value */
if ((str = ele_val.get_as_string(vmg0_)) == 0)
err_throw(VMERR_STRING_VAL_REQD);
/* set this string */
getp_add_string(vmg_ self, str, obj, voc_prop);
/*
* refresh the list pointer, in case it was const pool data
* and got swapped out by resolving the string pointer
*/
lst = str_val.get_as_list(vmg0_);
}
}
else if ((str = str_val.get_as_string(vmg0_)) != 0)
{
/* add the string */
getp_add_string(vmg_ self, str, obj, voc_prop);
}
else
{
/* string value required */
err_throw(VMERR_STRING_VAL_REQD);
}
/* there's no return value */
val->set_nil();
/* success */
return TRUE;
}
/*
* Service routine for getp_add() - add a single string
*/
void CVmObjDict::getp_add_string(VMG_ vm_obj_id_t self,
const char *str, vm_obj_id_t obj,
vm_prop_id_t voc_prop)
{
size_t len;
int added;
/* get the string's length prefix */
len = vmb_get_len(str);
str += 2;
/* add the entry */
added = add_hash_entry(vmg_ str, len, TRUE, obj, voc_prop, FALSE);
/*
* if there's a global undo object, and we actually added a new
* entry, add undo for the change
*/
if (added && G_undo != 0)
{
dict_undo_rec *undo_rec;
/* create the undo record */
undo_rec = alloc_undo_rec(DICT_UNDO_ADD, str, len);
undo_rec->obj = obj;
undo_rec->prop = voc_prop;
/* add the undo record */
add_undo_rec(vmg_ self, undo_rec);
}
/* mark the object as modified since load */
get_ext()->modified_ = TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* property evaluation - delete a word
*/
int CVmObjDict::getp_del(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc)
{
vm_obj_id_t obj;
vm_val_t str_val;
vm_prop_id_t voc_prop;
const char *lst;
const char *str;
static CVmNativeCodeDesc desc(3);
/* check arguments */
if (get_prop_check_argc(val, argc, &desc))
return TRUE;
/* pop the object, word string, and property ID arguments */
obj = CVmBif::pop_obj_val(vmg0_);
G_stk->pop(&str_val);
voc_prop = CVmBif::pop_propid_val(vmg0_);
/* if the string argument is a list, add each word from the list */
if ((lst = str_val.get_as_list(vmg0_)) != 0)
{
size_t cnt;
size_t idx;
vm_val_t ele_val;
/* get and skip the list count prefix */
cnt = vmb_get_len(lst);
/* iterate over the list, using 1-based indices */
for (idx = 1 ; idx <= cnt ; ++idx)
{
/* get the next value from the list */
CVmObjList::index_list(vmg_ &ele_val, lst, idx);
/* get the next string value */
if ((str = ele_val.get_as_string(vmg0_)) == 0)
err_throw(VMERR_STRING_VAL_REQD);
/* remove this string */
getp_del_string(vmg_ self, str, obj, voc_prop);
/*
* refresh the list pointer, in case it was const pool data
* and got swapped out by resolving the string pointer
*/
lst = str_val.get_as_list(vmg0_);
}
}
else if ((str = str_val.get_as_string(vmg0_)) != 0)
{
/* remove the string */
getp_del_string(vmg_ self, str, obj, voc_prop);
}
else
{
/* string value required */
err_throw(VMERR_STRING_VAL_REQD);
}
/* there's no return value */
val->set_nil();
/* success */
return TRUE;
}
/*
* Service routine for getp_del - delete a string value
*/
void CVmObjDict::getp_del_string(VMG_ vm_obj_id_t self,
const char *str, vm_obj_id_t obj,
vm_prop_id_t voc_prop)
{
size_t len;
int deleted;
/* get the string's length prefix */
len = vmb_get_len(str);
str += 2;
/* delete this entry */
deleted = del_hash_entry(vmg_ str, len, obj, voc_prop);
/*
* if there's a global undo object, and we actually deleted an
* entry, add undo for the change
*/
if (deleted && G_undo != 0)
{
dict_undo_rec *undo_rec;
/* create the undo record */
undo_rec = alloc_undo_rec(DICT_UNDO_DEL, str, len);
undo_rec->obj = obj;
undo_rec->prop = voc_prop;
/* add the undo record */
add_undo_rec(vmg_ self, undo_rec);
}
/* mark the object as modified since load */
get_ext()->modified_ = TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* callback context for is_defined enumeration
*/
struct isdef_ctx
{
isdef_ctx(VMG_ CVmObjDict *dict_obj, vm_val_t *filter,
const vm_val_t *val, const char *str, size_t len)
{
/* remember the VM globals and dictionary object */
globals = VMGLOB_ADDR;
dict = dict_obj;
/* we haven't yet found a match */
found = FALSE;
/* remember the string */
this->strval = *val;
this->strp = str;
this->strl = len;
/* remember the filter function */
filter_func = filter;
}
/* flag: we've found a match */
int found;
/* string value we're matching */
vm_val_t strval;
const char *strp;
size_t strl;
/* our filter callback function, if any */
const vm_val_t *filter_func;
/* VM globals */
vm_globals *globals;
/* dictionary we're searching */
CVmObjDict *dict;
};
/*
* Callback for getp_is_defined hash match enumeration
*/
void CVmObjDict::isdef_cb(void *ctx0, CVmHashEntry *entry0)
{
isdef_ctx *ctx = (isdef_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_val_t match_val;
VMGLOB_PTR(ctx->globals);
/*
* if we've already found a match, don't bother checking anything else
* - it could be non-trivial to do the string match, so we can save
* some work by skipping that if we've already found a match
* previously
*/
if (ctx->found)
return;
/*
* if this entry matches the search string, and it has at least one
* defined object/property associated with it, count it as a match
*/
if (entry->get_head() != 0
&& ctx->dict->match_strings(vmg_ &ctx->strval, ctx->strp, ctx->strl,
entry->getstr(), entry->getlen(),
&match_val))
{
/* if there's a filter function to invoke, check with it */
if (ctx->filter_func->typ != VM_NIL)
{
vm_val_t *valp;
/* push the match value parameter */
G_stk->push(&match_val);
/* call the filter callback */
G_interpreter->call_func_ptr(vmg_ ctx->filter_func, 1,
"Dictionary.isDefined", 0);
/* get the result */
valp = G_interpreter->get_r0();
/* if it's 0 or nil, it's a rejection of the match */
if (valp->typ == VM_NIL
|| (valp->typ == VM_INT && valp->val.intval == 0))
{
/* it's a rejection of the value - don't count it */
}
else
{
/* it's an acceptance of the value - count it as found */
ctx->found = TRUE;
}
}
else
{
/* there's no filter, so everything matches */
ctx->found = TRUE;
}
}
}
/*
* property evaluation - check to see if a word is defined
*/
int CVmObjDict::getp_is_defined(VMG_ vm_obj_id_t self, vm_val_t *val,
uint *argc)
{
static CVmNativeCodeDesc desc(1, 1);
uint orig_argc = (argc == 0 ? 0 : *argc);
vm_val_t *arg0;
const char *strp;
size_t strl;
vm_val_t filter;
unsigned int hash;
/* check arguments */
if (get_prop_check_argc(val, argc, &desc))
return TRUE;
/*
* make sure the argument is a string, but leave it on the stack for
* gc protection
*/
arg0 = G_stk->get(0);
if ((strp = arg0->get_as_string(vmg0_)) == 0)
err_throw(VMERR_STRING_VAL_REQD);
/* read and skip the string's length prefix */
strl = vmb_get_len(strp);
strp += VMB_LEN;
/* if there's a second argument, it's the filter function */
if (orig_argc >= 2)
{
/* retrieve the filter */
filter = *G_stk->get(1);
}
else
{
/* there's no filter */
filter.set_nil();
}
/* calculate the hash code */
hash = calc_str_hash(vmg_ arg0, strp, strl);
/* enumerate matches for the string */
isdef_ctx ctx(vmg_ this, &filter, arg0, strp, strl);
get_ext()->hashtab_->enum_hash_matches(hash, &isdef_cb, &ctx);
/* if we found any matches, return true; otherwise, return nil */
val->set_logical(ctx.found);
/* discard arguments */
G_stk->discard(orig_argc);
/* success */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* callback context for forEachWord enumerator
*/
struct for_each_word_enum_cb_ctx
{
/* callback function value */
vm_val_t *cb_val;
/* globals */
vm_globals *vmg;
};
/*
* callback for forEachWord enumerator
*/
static void for_each_word_enum_cb(void *ctx0, CVmHashEntry *entry0)
{
for_each_word_enum_cb_ctx *ctx = (for_each_word_enum_cb_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
size_t list_idx;
vm_obj_id_t str_obj;
/* set up access to the VM globals through my stashed context */
VMGLOB_PTR(ctx->vmg);
/* create a string object for this entry's word string */
str_obj = CVmObjString::create(vmg_ FALSE,
entry->getstr(), entry->getlen());
/* scan the entry's list of word associations */
for (list_idx = 0 ; ; ++list_idx)
{
vm_dict_entry *p;
size_t i;
/*
* Find the list entry at the current list index. Note that we
* scan the list on each iteration for the current index because
* we don't want to keep track of any pointers between iterations;
* this insulates us from any changes to the underlying list made
* in the callback.
*/
for (i = 0, p = entry->get_head() ; i < list_idx && p != 0 ;
p = p->nxt_, ++i) ;
/*
* if we didn't find a list entry at this index, we're done with
* this hashtable entry
*/
if (p == 0)
break;
/*
* Invoke the callback on this list entry. The arguments to the
* callback are the object, the string, and the vocabulary
* property of the association:
*
* func(obj, str, prop)
*
* Note that the order of the arguments is the same as for
* addWord() and removeWord().
*/
/* push the vocabulary property of the association */
G_stk->push()->set_propid(p->prop_);
/* push the string of the entry */
G_stk->push()->set_obj(str_obj);
/* push the object of the association */
G_stk->push()->set_obj(p->obj_);
/* invoke the callback */
G_interpreter->call_func_ptr(vmg_ ctx->cb_val, 3,
"Dictionary.forEachWord", 0);
}
}
/*
* property evaluation - enumerate each word association
*/
int CVmObjDict::getp_for_each_word(VMG_ vm_obj_id_t self, vm_val_t *retval,
uint *argc)
{
static CVmNativeCodeDesc desc(1);
vm_val_t *cb_val;
for_each_word_enum_cb_ctx ctx;
/* check arguments */
if (get_prop_check_argc(retval, argc, &desc))
return TRUE;
/* get a pointer to the callback value, but leave it on the stack */
cb_val = G_stk->get(0);
/* push a self-reference for gc protection while we're working */
G_stk->push()->set_obj(self);
/*
* enumerate the entries in our underlying hashtable - use the "safe"
* enumerator to ensure that we're insulated from any changes to the
* hashtable that the callback wants to make
*/
ctx.vmg = VMGLOB_ADDR;
ctx.cb_val = cb_val;
get_ext()->hashtab_->safe_enum_entries(for_each_word_enum_cb, &ctx);
/* discard arguments and gc protection */
G_stk->discard(2);
/* there's no return value */
retval->set_nil();
/* handled */
return TRUE;
}
/* ------------------------------------------------------------------------ */
/*
* Create an undo record
*/
dict_undo_rec *CVmObjDict::alloc_undo_rec(dict_undo_action action,
const char *txt, size_t len)
{
size_t alloc_size;
dict_undo_rec *rec;
/*
* compute the allocation size - start with the structure size, and
* add in the length we need to store the string if one is present
*/
alloc_size = sizeof(dict_undo_rec) - 1;
if (txt != 0)
alloc_size += len;
/* allocate the record */
rec = (dict_undo_rec *)t3malloc(alloc_size);
/* set the action */
rec->action = action;
/* if there's a word, copy the text */
if (txt != 0)
memcpy(rec->txt, txt, len);
/* save the word length */
rec->len = len;
/* presume we won't store an object or property */
rec->obj = VM_INVALID_OBJ;
rec->prop = VM_INVALID_PROP;
/* return the new record */
return rec;
}
/*
* add an undo record
*/
void CVmObjDict::add_undo_rec(VMG_ vm_obj_id_t self, dict_undo_rec *rec)
{
vm_val_t val;
/* add the record with an empty value */
val.set_empty();
if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &val))
{
/*
* we didn't add an undo record, so our extra undo information
* isn't actually going to be stored in the undo system - hence
* we must delete our extra information
*/
t3free(rec);
}
}
/*
* apply undo
*/
void CVmObjDict::apply_undo(VMG_ CVmUndoRecord *undo_rec)
{
if (undo_rec->id.ptrval != 0)
{
dict_undo_rec *rec;
/* get my undo record */
rec = (dict_undo_rec *)undo_rec->id.ptrval;
/* take the appropriate action */
switch(rec->action)
{
case DICT_UNDO_ADD:
/* we added the word, so we must now delete it */
del_hash_entry(vmg_ rec->txt, rec->len, rec->obj, rec->prop);
break;
case DICT_UNDO_DEL:
/* we deleted the word, so now we must add it */
add_hash_entry(vmg_ rec->txt, rec->len, TRUE,
rec->obj, rec->prop, FALSE);
break;
case DICT_UNDO_COMPARATOR:
/* we changed the comparator object, so change it back */
set_comparator(vmg_ rec->obj);
break;
}
/* discard my record */
t3free(rec);
/* clear the pointer in the undo record so we know it's gone */
undo_rec->id.ptrval = 0;
}
}
/*
* discard extra undo information
*/
void CVmObjDict::discard_undo(VMG_ CVmUndoRecord *rec)
{
/* delete our extra information record */
if (rec->id.ptrval != 0)
{
/* free the record */
t3free((dict_undo_rec *)rec->id.ptrval);
/* clear the pointer so we know it's gone */
rec->id.ptrval = 0;
}
}
/*
* mark references in an undo record
*/
void CVmObjDict::mark_undo_ref(VMG_ CVmUndoRecord *undo_rec)
{
/* check our private record if we have one */
if (undo_rec->id.ptrval != 0)
{
dict_undo_rec *rec;
/* get my undo record */
rec = (dict_undo_rec *)undo_rec->id.ptrval;
/* take the appropriate action */
switch(rec->action)
{
case DICT_UNDO_COMPARATOR:
/* the 'obj' entry is the old comparator object */
G_obj_table->mark_all_refs(rec->obj, VMOBJ_REACHABLE);
break;
case DICT_UNDO_ADD:
case DICT_UNDO_DEL:
/*
* these actions use only weak references, so there's nothing
* to do here
*/
break;
}
}
}
/*
* remove stale weak references from an undo record
*/
void CVmObjDict::remove_stale_undo_weak_ref(VMG_ CVmUndoRecord *undo_rec)
{
/* check our private record if we have one */
if (undo_rec->id.ptrval != 0)
{
dict_undo_rec *rec;
/* get my undo record */
rec = (dict_undo_rec *)undo_rec->id.ptrval;
/* take the appropriate action */
switch(rec->action)
{
case DICT_UNDO_ADD:
case DICT_UNDO_DEL:
/* check to see if our object is being deleted */
if (rec->obj != VM_INVALID_OBJ
&& G_obj_table->is_obj_deletable(rec->obj))
{
/*
* the object is being deleted - since we only weakly
* reference our objects, we must forget about this
* object now, which means we can forget this entire
* record
*/
t3free(rec);
undo_rec->id.ptrval = 0;
}
break;
case DICT_UNDO_COMPARATOR:
/* this action does not use weak references */
break;
}
}
}
/* ------------------------------------------------------------------------ */
/*
* callback context for weak reference function
*/
struct enum_hashtab_ctx
{
/* global context pointer */
vm_globals *vmg;
/* dictionary object */
CVmObjDict *dict;
};
/*
* Mark references
*/
void CVmObjDict::mark_refs(VMG_ uint state)
{
/* if I have a comparator object, mark it as referenced */
if (get_ext()->comparator_ != VM_INVALID_OBJ)
G_obj_table->mark_all_refs(get_ext()->comparator_, state);
}
/*
* Remove weak references that have become stale. For each object for
* which we have a weak reference, check to see if the object is marked
* for deletion; if so, remove our reference to the object.
*/
void CVmObjDict::remove_stale_weak_refs(VMG0_)
{
enum_hashtab_ctx ctx;
/* iterate over our hash table */
ctx.vmg = VMGLOB_ADDR;
ctx.dict = this;
get_ext()->hashtab_->enum_entries(&remove_weak_ref_cb, &ctx);
}
/*
* enumeration callback to eliminate stale weak references
*/
void CVmObjDict::remove_weak_ref_cb(void *ctx0, CVmHashEntry *entry0)
{
enum_hashtab_ctx *ctx = (enum_hashtab_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_dict_entry *cur;
vm_dict_entry *nxt;
/* set up for global access through the context */
VMGLOB_PTR(ctx->vmg);
/* scan the entry's list */
for (cur = entry->get_head() ; cur != 0 ; cur = nxt)
{
/* remember the next entry in case we delete this one */
nxt = cur->nxt_;
/* check to see if this object is free - if so, remove this record */
if (G_obj_table->is_obj_deletable(cur->obj_))
{
/* delete the entry */
entry->del_entry(ctx->dict->get_ext()->hashtab_,
cur->obj_, cur->prop_);
}
}
}
/* ------------------------------------------------------------------------ */
/*
* load from an image file
*/
void CVmObjDict::load_from_image(VMG_ vm_obj_id_t self,
const char *ptr, size_t siz)
{
/* remember where our image data comes from */
get_ext()->image_data_ = ptr;
get_ext()->image_data_size_ = siz;
/* build the hash table from the image data */
build_hash_from_image(vmg0_);
/*
* register for post-load initialization, as we might need to rebuild
* our hash table once we have access to the comparator object
*/
G_obj_table->request_post_load_init(self);
}
/*
* Post-load initialization
*/
void CVmObjDict::post_load_init(VMG_ vm_obj_id_t self)
{
vm_obj_id_t comp;
/*
* Rebuild the hash table with the comparator, if we have one. When
* we load, reset, or restore, we build a tentative hash table with no
* comparator (i.e., the default exact literal comparator), because we
* can't assume we have access to the comparator object (as it might
* be loaded after us). So, we register for post-load initialization,
* and then we go back and re-install the comparator for real,
* rebuilding the hash table with the actual comparator.
*/
if ((comp = get_ext()->comparator_) != VM_INVALID_OBJ)
{
/* ensure the comparator object is initialized */
G_obj_table->ensure_post_load_init(vmg_ comp);
/* set the comparator object type, now that it's loaded */
set_comparator_type(vmg_ comp);
/*
* force a rebuild the hash table, so that we build it with the
* comparator properly installed
*/
create_hash_table(vmg0_);
}
}
/*
* Build the hash table from the image data
*/
void CVmObjDict::build_hash_from_image(VMG0_)
{
uint cnt;
uint i;
const char *p;
const char *endp;
vm_obj_id_t comp;
/* start reading at the start of the image data */
p = get_ext()->image_data_;
endp = p + get_ext()->image_data_size_;
/* read the comparator object ID */
comp = (vm_obj_id_t)t3rp4u(p);
p += 4;
/*
* Do NOT install the actual comparator object at this point, but
* simply build the table tentatively with a nil comparator. We can't
* assume that the comparator object has been loaded yet (as it might
* be loaded after us), so we cannot use it to build the hash table.
* We have to do something with the data, though, so build the hash
* table tentatively with no comparator; we'll rebuild it again at
* post-load-init time with the real comparator.
*
* (This isn't as inefficient as it sounds, as we retain all of the
* original hash table entries when we rebuild the table. All we end
* up doing is scanning the entries again to recompute their new hash
* values, and reallocating the hash table object itself.)
*/
get_ext()->comparator_ = VM_INVALID_OBJ;
set_comparator_type(vmg_ VM_INVALID_OBJ);
/* create the new hash table */
create_hash_table(vmg0_);
/* read the entry count */
cnt = osrp2(p);
p += 2;
/* scan the entries */
for (i = 0 ; p < endp && i < cnt ; ++i)
{
vm_obj_id_t obj;
vm_prop_id_t prop;
uint key_len;
uint item_cnt;
const char *key;
CVmHashEntryDict *entry;
char key_buf[256];
char *keyp;
size_t key_rem;
/* read the key length */
key_len = *(unsigned char *)p++;
/* remember the key pointer */
key = p;
p += key_len;
/* copy the key to our buffer for decoding */
memcpy(key_buf, key, key_len);
/* decode the key */
for (keyp = key_buf, key_rem = key_len ; key_rem != 0 ;
--key_rem, ++keyp)
*keyp ^= 0xBD;
/* read the key from the decoded buffer now */
key = key_buf;
/* read the item count */
item_cnt = osrp2(p);
p += 2;
/* find an existing entry for this key */
entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(key, key_len);
/* if there's no existing entry, add one */
if (entry == 0)
{
/* create a new entry */
entry = new CVmHashEntryDict(key, key_len, TRUE, TRUE);
/* add it to the table */
get_ext()->hashtab_->add(entry);
}
/* read the items */
for ( ; item_cnt != 0 ; --item_cnt)
{
/* read the object ID and property ID */
obj = (vm_obj_id_t)t3rp4u(p);
prop = (vm_prop_id_t)osrp2(p + 4);
/*
* add the entry - this entry is from the image file, so
* mark it as such
*/
entry->add_entry(obj, prop, TRUE);
/* move on to the next item */
p += 6;
}
}
/*
* Now that we're done building the table, remember the comparator. We
* can't set the type yet, because the object might not be loaded yet -
* defer this until post-load initialization time.
*/
get_ext()->comparator_ = comp;
}
/* ------------------------------------------------------------------------ */
/*
* Add an entry
*/
int CVmObjDict::add_hash_entry(VMG_ const char *p, size_t len, int copy,
vm_obj_id_t obj, vm_prop_id_t prop,
int from_image)
{
CVmHashEntryDict *entry;
/* find an existing entry for this key */
entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(p, len);
/* if we didn't find an entry, add one */
if (entry == 0)
{
/* create an entry */
entry = new CVmHashEntryDict(p, len, copy, from_image);
/* add it to the table */
get_ext()->hashtab_->add(entry);
}
/* add the obj/prop to the entry's item list */
return entry->add_entry(obj, prop, from_image);
}
/* ------------------------------------------------------------------------ */
/*
* delete an entry
*/
int CVmObjDict::del_hash_entry(VMG_ const char *str, size_t len,
vm_obj_id_t obj, vm_prop_id_t voc_prop)
{
CVmHashEntryDict *entry;
/* find the hash table entry for the word */
entry = (CVmHashEntryDict *)get_ext()->hashtab_->find(str, len);
/* if we found it, delete the obj/prop entry */
if (entry != 0)
{
/* delete the entry */
return entry->del_entry(get_ext()->hashtab_, obj, voc_prop);
}
/* we didn't find anything to delete */
return FALSE;
}
/* ------------------------------------------------------------------------ */
/*
* restore to image file state
*/
void CVmObjDict::reset_to_image(VMG_ vm_obj_id_t self)
{
/* delete the hash table */
if (get_ext()->hashtab_ != 0)
{
/* delete it */
delete get_ext()->hashtab_;
/* forget it */
get_ext()->hashtab_ = 0;
}
/* rebuild the hash table from the image file data */
build_hash_from_image(vmg0_);
/*
* register for post-load initialization, as we might need to rebuild
* our hash table once we have access to the comparator object
*/
G_obj_table->request_post_load_init(self);
}
/* ------------------------------------------------------------------------ */
/*
* determine if the object has been changed since it was loaded
*/
int CVmObjDict::is_changed_since_load() const
{
/*
* return true if our 'modified' flag is true - if it is, we must have
* been changed since we were loaded
*/
return (get_ext()->modified_ != 0);
}
/* ------------------------------------------------------------------------ */
/*
* save-file enumeration context
*/
struct save_file_ctx
{
/* file object */
CVmFile *fp;
/* entry count */
ulong cnt;
/* globals */
vm_globals *vmg;
};
/*
* save to a file
*/
void CVmObjDict::save_to_file(VMG_ CVmFile *fp)
{
long cnt_pos;
long end_pos;
save_file_ctx ctx;
/* write the current comparator object */
fp->write_int4(get_ext()->comparator_);
/* remember the starting seek position and write a placeholder count */
cnt_pos = fp->get_pos();
fp->write_int4(0);
/* enumerate the entries to write out each one */
ctx.fp = fp;
ctx.cnt = 0;
ctx.vmg = VMGLOB_ADDR;
get_ext()->hashtab_->enum_entries(&save_file_cb, &ctx);
/* remember our position for a moment */
end_pos = fp->get_pos();
/* go back and write out the symbol count prefix */
fp->set_pos(cnt_pos);
fp->write_int4(ctx.cnt);
/* seek back to the end */
fp->set_pos(end_pos);
}
/*
* save file enumeration callback
*/
void CVmObjDict::save_file_cb(void *ctx0, CVmHashEntry *entry0)
{
save_file_ctx *ctx = (save_file_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_dict_entry *cur;
uint cnt;
/* set up global access */
VMGLOB_PTR(ctx->vmg);
/* write out this entry's name */
ctx->fp->write_int2(entry->getlen());
ctx->fp->write_bytes(entry->getstr(), entry->getlen());
/* count the items in this entry's list */
for (cnt = 0, cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/*
* if the referenced object is persistent, count this item;
* don't count items that refer to non-persistent items, because
* these objects will not be saved to the file and hence will
* not be present when the saved state is restored
*/
if (G_obj_table->is_obj_persistent(cur->obj_))
++cnt;
}
/*
* if there are no saveable items for this entry, do not write out
* the entry at all
*/
if (cnt == 0)
return;
/* count this entry */
++ctx->cnt;
/* write the item count */
ctx->fp->write_int2(cnt);
/* write out each item in this entry's list */
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/* if this item's object is persistent, write out the item */
if (G_obj_table->is_obj_persistent(cur->obj_))
{
/* write out the object and property for this entry */
ctx->fp->write_int4((long)cur->obj_);
ctx->fp->write_int2((int)cur->prop_);
}
}
}
/* ------------------------------------------------------------------------ */
/*
* restore from a file
*/
void CVmObjDict::restore_from_file(VMG_ vm_obj_id_t self,
CVmFile *fp, CVmObjFixup *fixups)
{
ulong cnt;
vm_obj_id_t comp;
/* delete the old hash table if we have one */
if (get_ext()->hashtab_ != 0)
{
delete get_ext()->hashtab_;
get_ext()->hashtab_ = 0;
}
/*
* Read the comparator and fix it up to the new object numbering
* scheme, but do not install it (as it might not be loaded yet).
*/
comp = fixups->get_new_id(vmg_ (vm_obj_id_t)fp->read_uint4());
/* create the new, empty hash table */
create_hash_table(vmg0_);
/* read the number of symbols */
cnt = fp->read_uint4();
/* read the symbols */
for ( ; cnt != 0 ; --cnt)
{
uint len;
uint read_len;
char buf[256];
uint item_cnt;
/* read the symbol length */
len = fp->read_uint2();
/* limit the reading to our buffer size */
read_len = len;
if (read_len > sizeof(buf))
read_len = sizeof(buf);
/* read the string */
fp->read_bytes(buf, read_len);
/* skip any extra data */
if (len > read_len)
fp->set_pos(fp->get_pos() + len - read_len);
/* read the item count */
item_cnt = fp->read_uint2();
/* read the items */
for ( ; item_cnt != 0 ; --item_cnt)
{
vm_obj_id_t obj;
vm_prop_id_t prop;
/* read the object and property ID's */
obj = (vm_obj_id_t)fp->read_uint4();
prop = (vm_prop_id_t)fp->read_uint2();
/* translate the object ID through the fixup table */
obj = fixups->get_new_id(vmg_ obj);
/* add the entry, if it refers to a valid object */
if (obj != VM_INVALID_OBJ)
add_hash_entry(vmg_ buf, len, TRUE, obj, prop, FALSE);
}
}
/*
* install the comparator (we can't set its type yet, though, because
* the object might not be loaded yet)
*/
get_ext()->comparator_ = comp;
set_comparator_type(vmg_ VM_INVALID_OBJ);
/*
* If we had to restore it, we'll have to save it if the current state
* is later saved, even if we don't modify it again. The fact that
* this version was saved before means this version has to be saved
* from now on.
*/
get_ext()->modified_ = TRUE;
/*
* register for post-load initialization, so that we can rebuild the
* hash table with the actual comparator if necessary
*/
G_obj_table->request_post_load_init(self);
}
/* ------------------------------------------------------------------------ */
/*
* impedence-matcher callback context
*/
struct enum_word_props_ctx
{
/* client callback to invoke */
void (*cb_func)(VMG_ void *ctx, vm_prop_id_t prop,
const vm_val_t *match_val);
/* string value to match */
const vm_val_t *strval;
const char *strp;
size_t strl;
/* client callback context */
void *cb_ctx;
/* globals */
vm_globals *globals;
/* the dictionary object we're searching */
CVmObjDict *dict;
};
/*
* enum_word_props hash table enumeration callback
*/
void CVmObjDict::enum_word_props_cb(void *ctx0, CVmHashEntry *entry0)
{
enum_word_props_ctx *ctx = (enum_word_props_ctx *)ctx0;
CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
vm_val_t match_val;
VMGLOB_PTR(ctx->globals);
/* if this entry matches the search string, process it */
if (ctx->dict->match_strings(vmg_ ctx->strval, ctx->strp, ctx->strl,
entry->getstr(), entry->getlen(),
&match_val))
{
vm_dict_entry *cur;
/* process the items under this entry */
for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
{
/* invoke the callback */
(*ctx->cb_func)(vmg_ ctx->cb_ctx, cur->prop_, &match_val);
}
}
}
/*
* Enumerate the properties for which a word is defined
*/
void CVmObjDict::enum_word_props(VMG_
void (*cb_func)(VMG_ void *, vm_prop_id_t,
const vm_val_t *),
void *cb_ctx, const vm_val_t *strval,
const char *strp, size_t strl)
{
enum_word_props_ctx ctx;
unsigned int hash;
/* set up the enumeration callback context */
ctx.strval = strval;
ctx.strp = strp;
ctx.strl = strl;
ctx.cb_func = cb_func;
ctx.cb_ctx = cb_ctx;
ctx.globals = VMGLOB_ADDR;
ctx.dict = this;
/* calculate the hash code */
hash = calc_str_hash(vmg_ strval, strp, strl);
/* enumerate the matches */
get_ext()->hashtab_->enum_hash_matches(hash, &enum_word_props_cb, &ctx);
}
|