cfad47cfa3/tads3/vmdict.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header$ */
2
3
/* 
4
 *   Copyright (c) 2000, 2002 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   Please see the accompanying license file, LICENSE.TXT, for information
7
 *   on using and copying this software.  
8
 */
9
/*
10
Name
11
  vmdict.h - VM 'Dictionary' metaclass
12
Function
13
  
14
Notes
15
  
16
Modified
17
  01/24/00 MJRoberts  - Creation
18
*/
19
20
#ifndef VMDICT_H
21
#define VMDICT_H
22
23
#include <stdlib.h>
24
#include <string.h>
25
26
#include "t3std.h"
27
#include "vmtype.h"
28
#include "vmglob.h"
29
#include "vmobj.h"
30
#include "vmundo.h"
31
#include "vmhash.h"
32
33
34
/* forward-declare the class */
35
class CVmObjDict;
36
37
/* ------------------------------------------------------------------------ */
38
/* 
39
 *   undo action codes 
40
 */
41
enum dict_undo_action
42
{
43
    /* we added this word to the dictionary (undo by deleting it) */
44
    DICT_UNDO_ADD,
45
46
    /* we deleted this word from the dictionary (undo by adding it back) */
47
    DICT_UNDO_DEL,
48
49
    /* we change the comparator object */
50
    DICT_UNDO_COMPARATOR
51
};
52
53
54
/* ------------------------------------------------------------------------ */
55
/*
56
 *   The image file data block is arranged as follows:
57
 *   
58
 *.  UINT4 comparator_object_id
59
 *.  UINT2 load_image_entry_count
60
 *.  entry 1
61
 *.  entry 2
62
 *.  ...
63
 *.  entry N
64
 *   
65
 *   Each entry has the following structure:
66
 *   
67
 *.  UCHAR key_string_byte_length
68
 *.  key_string (UTF-8 characters, not null terminated, XOR'ed with 0xBD)
69
 *.  UINT2 number of sub-entries
70
 *.  sub-entry 1
71
 *.  sub-entry 2
72
 *.  etc
73
 *   
74
 *   Each sub-entry is structured like this:
75
 *   
76
 *   UINT4 associated_object_id
77
 *.  UINT2 defining_property_id
78
 *   
79
 *   Note that each byte of the key string is XOR'ed with the arbitrary
80
 *   byte value 0xBD.  This is simply to provide a minimal level of
81
 *   obfuscation in the image file to prevent casual browsing of the image
82
 *   contents.  
83
 */
84
/*   
85
 *   Separately, we maintain a hash table and entries in the hash table.
86
 *   We don't attempt to keep any of the other data in the object in a
87
 *   portable internal format, but rather serialize data to a saved state
88
 *   file and rebuild the internal format on demand.  We also do not keep
89
 *   the hash table in the variable heap mechanism, but simply use the
90
 *   standard system heap.  The internal structure of our hash table would
91
 *   be too complicated to keep in the variable heap in portable format,
92
 *   and furthermore we do not expect dictionary instances to be numerous
93
 *   (hence the cost of serializing should be small, since it won't be
94
 *   repeated over many objects) or frequently changed (hence keeping
95
 *   these in the standard system heap should not cause excessive heap
96
 *   fragmentation).
97
 */
98
99
/* comparator object types */
100
enum vm_dict_comp_type
101
{
102
    VMDICT_COMP_NONE,                               /* no comparator object */
103
    VMDICT_COMP_STRCOMP,                                /* StringComparator */
104
    VMDICT_COMP_GENERIC                        /* generic comparator object */
105
};
106
107
/*
108
 *   Dictionary object extension - we use this memory (in the variable
109
 *   heap) to keep track of our image data and our hash table.  
110
 */
111
struct vm_dict_ext
112
{
113
    /* pointer to load image data, if any */
114
    const char *image_data_;
115
    size_t image_data_size_;
116
117
    /* pointer to our hash table (in the system heap) */
118
    class CVmHashTable *hashtab_;
119
120
    /* flag: the table has been changed since image load */
121
    ulong modified_;
122
123
    /* our comparator object, if any */
124
    vm_obj_id_t comparator_;
125
    CVmObject *comparator_obj_;
126
127
    /* type of comparator */
128
    vm_dict_comp_type comparator_type_;
129
};
130
131
/* ------------------------------------------------------------------------ */
132
/*
133
 *   Dictionary object interface 
134
 */
135
class CVmObjDict: public CVmObject
136
{
137
    friend class CVmMetaclassDict;
138
139
public:
140
    /* metaclass registration object */
141
    static class CVmMetaclass *metaclass_reg_;
142
    class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; }
143
144
    /* am I of the given metaclass? */
145
    virtual int is_of_metaclass(class CVmMetaclass *meta) const
146
    {
147
        /* try my own metaclass and my base class */
148
        return (meta == metaclass_reg_
149
                || CVmObject::is_of_metaclass(meta));
150
    }
151
152
    /* create dynamically using stack arguments */
153
    static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr,
154
                                         uint argc);
155
156
    /* 
157
     *   call a static property - we don't have any of our own, so simply
158
     *   "inherit" the base class handling 
159
     */
160
    static int call_stat_prop(VMG_ vm_val_t *result,
161
                              const uchar **pc_ptr, uint *argc,
162
                              vm_prop_id_t prop)
163
        { return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop); }
164
165
    /* determine if an object is a Dictionary object */
166
    static int is_dictionary_obj(VMG_ vm_obj_id_t obj)
167
        { return vm_objp(vmg_ obj)->is_of_metaclass(metaclass_reg_); }
168
169
    /* notify of deletion */
170
    void notify_delete(VMG_ int in_root_set);
171
172
    /* get a property */
173
    int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val,
174
                 vm_obj_id_t self, vm_obj_id_t *source_obj, uint *argc);
175
176
    /* set a property */
177
    void set_prop(VMG_ class CVmUndo *undo,
178
                  vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val);
179
180
    /* receive notification of a new undo savepoint */
181
    void notify_new_savept() { }
182
183
    /* apply undo */
184
    void apply_undo(VMG_ struct CVmUndoRecord *rec);
185
186
    /* discard additional information associated with an undo record */
187
    void discard_undo(VMG_ struct CVmUndoRecord *rec);
188
189
    /* mark a reference in undo */
190
    void mark_undo_ref(VMG_ struct CVmUndoRecord *rec);
191
192
    /* remove stale weak references from an undo record */
193
    void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *rec);
194
195
    /* mark references */
196
    void mark_refs(VMG_ uint);
197
198
    /* remove weak references */
199
    void remove_stale_weak_refs(VMG0_);
200
201
    /* load from an image file */
202
    void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t siz);
203
204
    /* perform post-load initialization */
205
    void post_load_init(VMG_ vm_obj_id_t self);
206
207
    /* restore to image file state */
208
    void reset_to_image(VMG_ vm_obj_id_t /*self*/);
209
210
    /* determine if the object has been changed since it was loaded */
211
    int is_changed_since_load() const;
212
213
    /* save to a file */
214
    void save_to_file(VMG_ class CVmFile *fp);
215
216
    /* restore from a file */
217
    void restore_from_file(VMG_ vm_obj_id_t self,
218
                           class CVmFile *fp, class CVmObjFixup *);
219
220
    /* rebuild for image file */
221
    virtual ulong rebuild_image(VMG_ char *buf, ulong buflen);
222
223
    /* convert to constant data */
224
    virtual void convert_to_const_data(VMG_ class CVmConstMapper *mapper,
225
                                       vm_obj_id_t self);
226
227
    /* enumerate the properties for which a word is defined */
228
    void enum_word_props(VMG_ void (*cb_func)(VMG_ void *, vm_prop_id_t,
229
                                              const vm_val_t *),
230
                         void *cb_ctx, const vm_val_t *strval,
231
                         const char *strp, size_t strl);
232
233
    /* get/set my comparator object */
234
    vm_obj_id_t get_comparator() const { return get_ext()->comparator_; }
235
    void set_comparator(VMG_ vm_obj_id_t obj);
236
237
    /* 
238
     *   Match a pair of strings.
239
     *   
240
     *   'valstrval' is the vm_val_t with the string value, if one is
241
     *   available; if not, this can be given as null and we'll synthesize a
242
     *   new string object from 'valstr' and 'vallen' if one is needed.  We
243
     *   don't require the caller to synthesize a string object because we
244
     *   might not need one; we'll create one only if one is actually needed.
245
     *   
246
     *   'valstr' and 'vallen' directly give the text of the value string,
247
     *   and 'refstr' and 'reflen' likewise directly give the text of the
248
     *   reference string.  
249
     */
250
    int match_strings(VMG_ const vm_val_t *valstrval,
251
                      const char *valstr, size_t vallen,
252
                      const char *refstr, size_t reflen,
253
                      vm_val_t *result_val);
254
255
    /* 
256
     *   Calculate the hash value for a string.  As in match_strings(),
257
     *   'valstrval' can be passed as null if no vm_val_t for the string text
258
     *   is available, in which case we'll synthesize a new string object
259
     *   from 'valstr' and 'vallen' if one is needed. 
260
     */
261
    unsigned int calc_str_hash(VMG_ const vm_val_t *valstrval,
262
                               const char *valstr, size_t vallen);
263
264
protected:
265
    CVmObjDict(VMG0_);
266
267
    /* set the comparator type */
268
    void set_comparator_type(VMG_ vm_obj_id_t obj);
269
270
    /* create or re-create the hash table */
271
    void create_hash_table(VMG0_);
272
273
    /* fill the hash table with entries from the image data */
274
    void build_hash_from_image(VMG0_);
275
276
    /* property evaluation - undefined property */
277
    int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; }
278
279
    /* enumeration callback for getp_find */
280
    static void find_cb(void *ctx, class CVmHashEntry *entry);
281
282
    /* enumeration callback for getp_isdef */
283
    static void isdef_cb(void *ctx, class CVmHashEntry *entry);
284
285
    /* enumeration callback for enum_word_props */
286
    static void enum_word_props_cb(void *ctx, class CVmHashEntry *entry);
287
288
    /* property evaluation - set the comparator object */
289
    int getp_set_comparator(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc);
290
291
    /* property evaluation - findWord */
292
    int getp_find(VMG_ vm_obj_id_t, vm_val_t *val, uint *argc);
293
294
    /* property evaluation - addWord */
295
    int getp_add(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
296
297
    /* service routine for getp_add - add a single string */
298
    void getp_add_string(VMG_ vm_obj_id_t self,
299
                         const char *str, vm_obj_id_t obj,
300
                         vm_prop_id_t voc_prop);
301
302
    /* property evaluation - delWord */
303
    int getp_del(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
304
305
    /* service routine for getp_del - remove a single string */
306
    void getp_del_string(VMG_ vm_obj_id_t self,
307
                         const char *str, vm_obj_id_t obj,
308
                         vm_prop_id_t voc_prop);
309
310
    /* property evaluation - isWordDefined */
311
    int getp_is_defined(VMG_ vm_obj_id_t self, vm_val_t *val, uint *argc);
312
313
    /* property evaluation - forEachWord */
314
    int getp_for_each_word(VMG_ vm_obj_id_t self, vm_val_t *retval,
315
                           uint *argc);
316
317
    /* get my extension, properly cast */
318
    vm_dict_ext *get_ext() const { return (vm_dict_ext *)ext_; }
319
320
    /* 
321
     *   add an entry; returns true if successful, false if the entry
322
     *   already exists, in which case no new entry is made 
323
     */
324
    int add_hash_entry(VMG_ const char *p, size_t len, int copy,
325
                       vm_obj_id_t obj, vm_prop_id_t prop, int from_image);
326
327
    /* delete an entry */
328
    int del_hash_entry(VMG_ const char *p, size_t len,
329
                       vm_obj_id_t obj, vm_prop_id_t prop);
330
331
    /* callback for hash table enumeration - delete stale weak refs */
332
    static void remove_weak_ref_cb(void *ctx, class CVmHashEntry *entry);
333
334
    /* callback for hash table enumeration - save file */
335
    static void save_file_cb(void *ctx, class CVmHashEntry *entry);
336
    
337
    /* callback for hash table enumeration - convert to constant data */
338
    static void cvt_const_cb(void *ctx, class CVmHashEntry *entry);
339
340
    /* callback for hash table enumeration - rebuild image, phase 1 */
341
    static void rebuild_cb_1(void *ctx, class CVmHashEntry *entry);
342
343
    /* callback for hash table enumeration - rebuild image, phase 2 */
344
    static void rebuild_cb_2(void *ctx, class CVmHashEntry *entry);
345
346
    /* allocate an undo record */
347
    struct dict_undo_rec *alloc_undo_rec(enum dict_undo_action action,
348
                                         const char *txt, size_t len);
349
350
    /* add a record to the global undo stream */
351
    void add_undo_rec(VMG_ vm_obj_id_t self, struct dict_undo_rec *rec);
352
353
    /* function table */
354
    static int (CVmObjDict::*func_table_[])(VMG_ vm_obj_id_t self,
355
                                            vm_val_t *retval, uint *argc);
356
};
357
358
359
/* ------------------------------------------------------------------------ */
360
/*
361
 *   hash table list entry - each hash entry has a list of associated
362
 *   objects; this structure represents an entry in one of these lists 
363
 */
364
struct vm_dict_entry
365
{
366
    vm_dict_entry(vm_obj_id_t obj, vm_prop_id_t prop, int from_image)
367
    {
368
        obj_ = obj;
369
        prop_ = prop;
370
        from_image_ = from_image;
371
        nxt_ = 0;
372
    }
373
374
    /* object ID of this entry */
375
    vm_obj_id_t obj_;
376
377
    /* defining property ID */
378
    vm_prop_id_t prop_;
379
380
    /* next entry in the list */
381
    vm_dict_entry *nxt_;
382
383
    /* flag: entry is from the image file */
384
    int from_image_;
385
};
386
387
/*
388
 *   Dictionary object hash table entry 
389
 */
390
class CVmHashEntryDict: public CVmHashEntryCS
391
{
392
public:
393
    CVmHashEntryDict(const char *str, size_t len, int copy, int from_image)
394
        : CVmHashEntryCS(str, len, copy || from_image)
395
    {
396
        /* nothing in our item list yet */
397
        list_ = 0;
398
    }
399
400
    ~CVmHashEntryDict()
401
    {
402
        /* delete all of our entries */
403
        while (list_ != 0)
404
        {
405
            vm_dict_entry *nxt;
406
407
            /* note the next entry */
408
            nxt = list_->nxt_;
409
410
            /* delete the current entry */
411
            delete list_;
412
413
            /* move on to the next entry */
414
            list_ = nxt;
415
        }
416
    }
417
418
    /* get the first entry in our list */
419
    vm_dict_entry *get_head() const { return list_; }
420
421
    /* 
422
     *   add an entry to our list - returns true if we added the entry,
423
     *   false if an entry with the same object ID was already present, in
424
     *   which case we will ignore the addition 
425
     */
426
    int add_entry(vm_obj_id_t obj, vm_prop_id_t prop, int from_image)
427
    {
428
        vm_dict_entry *entry;
429
        vm_dict_entry *prv;
430
431
        /* 
432
         *   check to see if this entry is already in our list - if it is,
433
         *   don't bother adding the redundant definition 
434
         */
435
        for (prv = 0, entry = list_ ; entry != 0 ; entry = entry->nxt_)
436
        {
437
            /* 
438
             *   if this entry matches the object ID and property ID,
439
             *   ignore the addition 
440
             */
441
            if (entry->obj_ == obj && entry->prop_ == prop)
442
                return FALSE;
443
444
            /* 
445
             *   if this entry matches the property, remember it as the
446
             *   insertion point, so that we keep all definitions for the
447
             *   same word with the same property together in the list 
448
             */
449
            if (entry->prop_ == prop)
450
                prv = entry;
451
        }
452
453
        /* create a list entry */
454
        entry = new vm_dict_entry(obj, prop, from_image);
455
456
        /* 
457
         *   link it in after the insertion point, or at the head of our
458
         *   list if we didn't find any other insertion point 
459
         */
460
        if (prv != 0)
461
        {
462
            /* we found an insertion point - link it in */
463
            entry->nxt_ = prv->nxt_;
464
            prv->nxt_ = entry;
465
        }
466
        else
467
        {
468
            /* no insertion point - link it at the head of our list */
469
            entry->nxt_ = list_;
470
            list_ = entry;
471
        }
472
473
        /* we added the entry */
474
        return TRUE;
475
    }
476
477
    /* 
478
     *   Delete all entries matching a given object ID from our list.
479
     *   Returns true if any entries were deleted, false if not. 
480
     */
481
    int del_entry(CVmHashTable *table, vm_obj_id_t obj, vm_prop_id_t prop)
482
    {
483
        vm_dict_entry *cur;
484
        vm_dict_entry *nxt;
485
        vm_dict_entry *prv;
486
        int found;
487
488
        /* find the entry in our list */
489
        for (found = FALSE, prv = 0, cur = list_ ; cur != 0 ;
490
             prv = cur, cur = nxt)
491
        {
492
            /* remember the next entry */
493
            nxt = cur->nxt_;
494
495
            /* if this is our entry, delete it */
496
            if (cur->obj_ == obj && cur->prop_ == prop)
497
            {
498
                /* unlink this entry */
499
                if (prv != 0)
500
                    prv->nxt_ = nxt;
501
                else
502
                    list_ = nxt;
503
504
                /* delete this entry */
505
                delete cur;
506
507
                /* note that we found at least one entry to delete */
508
                found = TRUE;
509
            }
510
        }
511
512
        /* if our list is now empty, delete myself from the table */
513
        if (list_ == 0)
514
        {
515
            /* remove myself from the table */
516
            table->remove(this);
517
518
            /* delete myself */
519
            delete this;
520
        }
521
522
        /* tell the caller whether we found anything to delete */
523
        return found;
524
    }
525
526
protected:
527
    /* list of associated objects */
528
    vm_dict_entry *list_;
529
};
530
531
532
/* ------------------------------------------------------------------------ */
533
/*
534
 *   Registration table object 
535
 */
536
class CVmMetaclassDict: public CVmMetaclass
537
{
538
public:
539
    /* get the global name */
540
    const char *get_meta_name() const { return "dictionary2/030000"; }
541
542
    /* create from image file */
543
    void create_for_image_load(VMG_ vm_obj_id_t id)
544
        { new (vmg_ id) CVmObjDict(vmg0_); }
545
546
    /* create from restoring from saved state */
547
    void create_for_restore(VMG_ vm_obj_id_t id)
548
        { new (vmg_ id) CVmObjDict(vmg0_); }
549
550
    /* create dynamically using stack arguments */
551
    vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc)
552
        { return CVmObjDict::create_from_stack(vmg_ pc_ptr, argc); }
553
554
    /* call a static property */
555
    int call_stat_prop(VMG_ vm_val_t *result,
556
                       const uchar **pc_ptr, uint *argc,
557
                       vm_prop_id_t prop)
558
    {
559
        return CVmObjDict::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
560
    }
561
};
562
563
#endif /* VMDICT_H */
564
565
/*
566
 *   Register the class 
567
 */
568
VM_REGISTER_METACLASS(CVmObjDict)