~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/vmimgrb.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifdef RCSID
 
2
static char RCSid[] =
 
3
"$Header$";
 
4
#endif
 
5
 
 
6
/* 
 
7
 *   Copyright (c) 1999, 2002 Michael J. Roberts.  All Rights Reserved.
 
8
 *   
 
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
 
10
 *   on using and copying this software.  
 
11
 */
 
12
/*
 
13
Name
 
14
  vmimgrb.cpp - T3 Image File Re-Builder
 
15
Function
 
16
  This module re-builds an image file from the contents of memory after
 
17
  the program has been "pre-initialized."  This allows a program to
 
18
  run through its static state initialization during the compilation
 
19
  process, then store the result as a new image file with pre-initialized
 
20
  state.  Any initialization that must always happen for every run of
 
21
  the program can be performed during this pre-initialization pass,
 
22
  saving the time of doing the work each time the program is run.
 
23
 
 
24
  Mostly, we just copy the old image file to the new image file; most
 
25
  parts of the image file are copied without changes.  We update the
 
26
  object stream, replacing the original objects with the objects in
 
27
  their pre-initialized state, and we add any new strings dynamically
 
28
  created during pre-initialization to the constant pool.
 
29
Notes
 
30
  
 
31
Modified
 
32
  07/21/99 MJRoberts  - Creation
 
33
*/
 
34
 
 
35
#include <stdlib.h>
 
36
#include <memory.h>
 
37
 
 
38
#include "t3std.h"
 
39
#include "vmfile.h"
 
40
#include "vmimage.h"
 
41
#include "vmpool.h"
 
42
#include "vmglob.h"
 
43
#include "vmwrtimg.h"
 
44
#include "vmobj.h"
 
45
#include "vmtobj.h"
 
46
#include "vmdict.h"
 
47
#include "vmhash.h"
 
48
#include "vmlst.h"
 
49
#include "vmbignum.h"
 
50
#include "vmstr.h"
 
51
#include "vmgram.h"
 
52
#include "vmmeta.h"
 
53
#include "vmimgrb.h"
 
54
#include "vmintcls.h"
 
55
#include "vmiter.h"
 
56
#include "vmvec.h"
 
57
#include "vmlookup.h"
 
58
#include "vmstack.h"
 
59
#include "vmbytarr.h"
 
60
#include "vmcset.h"
 
61
#include "vmfilobj.h"
 
62
#include "vmpat.h"
 
63
#include "vmstrcmp.h"
 
64
 
 
65
 
 
66
/* ------------------------------------------------------------------------ */
 
67
/*
 
68
 *   Rebuild the OBJS blocks and write the data to the file.  This goes
 
69
 *   through the objects in memory and constructs fixed image-file
 
70
 *   versions of the objects, then writes them to OBJS blocks.  
 
71
 */
 
72
static void vm_rewrite_objs_blocks(VMG_ CVmImageWriter *writer,
 
73
                                   class CVmConstMapper *mapper)
 
74
{
 
75
    size_t i;
 
76
    
 
77
    /* rewrite the image block for each of our defined metaclasses */
 
78
    for (i = 0 ; i < G_meta_table->get_count() ; ++i)
 
79
    {
 
80
        /* write this metaclass's objects */
 
81
        G_obj_table->rebuild_image(vmg_ i, writer, mapper);
 
82
    }
 
83
}
 
84
 
 
85
/* ------------------------------------------------------------------------ */
 
86
/*
 
87
 *   Re-write the image file 
 
88
 */
 
89
void vm_rewrite_image(VMG_ CVmFile *origfp, CVmFile *newfp,
 
90
                      ulong static_cs_ofs)
 
91
{
 
92
    char buf[4096];
 
93
    int done;
 
94
    CVmImageWriter *writer;
 
95
    CVmConstMapper *const_mapper;
 
96
    uchar xor_mask;
 
97
    ulong code_page_size;
 
98
 
 
99
    /* we don't know the code page size yet */
 
100
    code_page_size = 0;
 
101
 
 
102
    /* choose an arbitrary XOR mask for our pages */
 
103
    xor_mask = 0xEF;
 
104
 
 
105
    /* create an image writer to help us write the output file */
 
106
    writer = new CVmImageWriter(newfp);
 
107
 
 
108
    /* create our constant mapper */
 
109
    const_mapper = new CVmConstMapper(vmg0_);
 
110
 
 
111
    /* 
 
112
     *   clear all undo information - we don't save undo with the rebuilt
 
113
     *   file, so there's no reason to keep any of the objects that are
 
114
     *   referenced only in the undo records 
 
115
     */
 
116
    G_undo->drop_undo(vmg0_);
 
117
 
 
118
    /* discard everything on the stack */
 
119
    G_stk->discard(G_stk->get_depth());
 
120
 
 
121
    /* 
 
122
     *   perform a full garbage collection pass, to make sure we don't
 
123
     *   include any unreachable objects in the new image file 
 
124
     */
 
125
    G_obj_table->gc_full(vmg0_);
 
126
 
 
127
    /* add any metaclasses that weren't in the dependency table originally */
 
128
    G_obj_table->add_metadeps_for_instances(vmg0_);
 
129
 
 
130
    /* convert objects to constant data to the extent possible */
 
131
    G_obj_table->rebuild_image_convert_const_data(vmg_ const_mapper);
 
132
 
 
133
    /* 
 
134
     *   copy the header (signature, UINT2 format version number, 32
 
135
     *   reserved bytes, 24-byte compilation timestamp) to the new file 
 
136
     */
 
137
    origfp->read_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
 
138
    newfp->write_bytes(buf, sizeof(VMIMAGE_SIG) - 1 + 2 + 32 + 24);
 
139
 
 
140
    /* copy or replace the data blocks */
 
141
    for (done = FALSE ; !done ; )
 
142
    {
 
143
        ulong siz;
 
144
 
 
145
        /* read the next block header */
 
146
        origfp->read_bytes(buf, 10);
 
147
 
 
148
        /* get the size */
 
149
        siz = t3rp4u(buf + 4);
 
150
 
 
151
        /* check the block type */
 
152
        if (CVmImageLoader::block_type_is(buf, "OBJS")
 
153
            || CVmImageLoader::block_type_is(buf, "MCLD")
 
154
            || CVmImageLoader::block_type_is(buf, "SINI"))
 
155
        {
 
156
            /* 
 
157
             *   Simply skip all of the original OBJS (object data) or
 
158
             *   MCLD (metaclass dependency table) blocks -- we'll replace
 
159
             *   them with our re-built blocks.
 
160
             *   
 
161
             *   Also skip SINI (static initializer) blocks, since these
 
162
             *   are only needed for pre-initialization and can be
 
163
             *   discarded from the final image file.  
 
164
             */
 
165
            origfp->set_pos(origfp->get_pos() + (long)siz);
 
166
        }
 
167
        else if (CVmImageLoader::block_type_is(buf, "CPDF"))
 
168
        {
 
169
            char cpdf_buf[20];
 
170
            uint pool_id;
 
171
            ulong pgcnt;
 
172
            ulong pgsiz;
 
173
            
 
174
            /* read the pool definition */
 
175
            origfp->read_bytes(cpdf_buf, 10);
 
176
 
 
177
            /* get the ID, page count, and page size from the definition */
 
178
            pool_id = osrp2(cpdf_buf);
 
179
            pgcnt = t3rp4u(cpdf_buf + 2);
 
180
            pgsiz = t3rp4u(cpdf_buf + 6);
 
181
 
 
182
            /* 
 
183
             *   if this is the constant pool (pool ID = 2), increase the
 
184
             *   page count by the extra constant pool pages we need for
 
185
             *   converting new object data to constants 
 
186
             */
 
187
            if (pool_id == 2)
 
188
            {
 
189
                /* add in our new count */
 
190
                pgcnt += const_mapper->get_page_count();
 
191
 
 
192
                /* write the new count */
 
193
                oswp4(cpdf_buf + 2, pgcnt);
 
194
            }
 
195
 
 
196
            /*
 
197
             *   if this is the code pool (pool ID = 1), and we have
 
198
             *   static initializers, decrease the page count to exclude
 
199
             *   the static initializer pages (all of the static
 
200
             *   initializers are grouped at the high end of the code
 
201
             *   pool, so we can discard them and only them by truncating
 
202
             *   the code pool before the page containing the first static
 
203
             *   initializer) 
 
204
             */
 
205
            if (pool_id == 1 && static_cs_ofs != 0)
 
206
            {
 
207
                /* 
 
208
                 *   calculate the new count - it's the offset to the
 
209
                 *   first static initializer divided by the size of each
 
210
                 *   code page 
 
211
                 */
 
212
                pgcnt = static_cs_ofs / pgsiz;
 
213
                
 
214
                /* write the new count */
 
215
                oswp4(cpdf_buf + 2, pgcnt);
 
216
 
 
217
                /* 
 
218
                 *   remember the code page size for later, when we're
 
219
                 *   scanning the code pages themselves 
 
220
                 */
 
221
                code_page_size = pgsiz;
 
222
            }
 
223
            
 
224
            /* update the constant block definition */
 
225
            newfp->write_bytes(buf, 10);
 
226
            newfp->write_bytes(cpdf_buf, 10);
 
227
        }
 
228
        else if (CVmImageLoader::block_type_is(buf, "CPPG"))
 
229
        {
 
230
            char cppg_buf[20];
 
231
            long start_pos;
 
232
            uint pool_id;
 
233
            ulong page_idx;
 
234
            int keep_block;
 
235
 
 
236
            /* presume we're going to keep this block */
 
237
            keep_block = TRUE;
 
238
            
 
239
            /* 
 
240
             *   This is a constant page - if it's in pool 2 (constants),
 
241
             *   use its XOR mask for any new pages we write.  First, read
 
242
             *   the pool header, then seek back so we can copy the block
 
243
             *   unchanged.  
 
244
             */
 
245
            start_pos = origfp->get_pos();
 
246
            origfp->read_bytes(cppg_buf, 7);
 
247
            origfp->set_pos(start_pos);
 
248
 
 
249
            /* get the pool ID and the page's index */
 
250
            pool_id = osrp2(cppg_buf);
 
251
            page_idx = t3rp4u(cppg_buf + 2);
 
252
 
 
253
            /* if it's pool 2 (constants), read its XOR mask byte */
 
254
            if (pool_id == 2)
 
255
                xor_mask = cppg_buf[6];
 
256
 
 
257
            /* 
 
258
             *   if it's pool 1 (code), and it's above the start of the
 
259
             *   static initializers, skip it - we don't want to copy
 
260
             *   static initializer code to the final image file, since
 
261
             *   they're only needed for static initialization, which we
 
262
             *   necessarily have finished by the time we reach this point 
 
263
             */
 
264
            if (pool_id == 1
 
265
                && static_cs_ofs != 0
 
266
                && page_idx * code_page_size >= static_cs_ofs)
 
267
            {
 
268
                /* this is a static initializer block - skip it */
 
269
                keep_block = FALSE;
 
270
            }
 
271
 
 
272
            /* keep or skip the block, as appropriate */
 
273
            if (keep_block)
 
274
            {
 
275
                /* 
 
276
                 *   we only wanted to get information from this block, so
 
277
                 *   go copy it as-is to the output 
 
278
                 */
 
279
                goto copy_block;
 
280
            }
 
281
            else
 
282
            {
 
283
                /* skip past the block */
 
284
                origfp->set_pos(origfp->get_pos() + (long)siz);
 
285
            }
 
286
        }
 
287
        else if (CVmImageLoader::block_type_is(buf, "EOF "))
 
288
        {
 
289
            /* end of file - note that we're done after this block */
 
290
            done = TRUE;
 
291
 
 
292
            /* re-write the metaclass dependency block */
 
293
            G_meta_table->rebuild_image(writer);
 
294
 
 
295
            /* write our new OBJS blocks */
 
296
            vm_rewrite_objs_blocks(vmg_ writer, const_mapper);
 
297
 
 
298
            /* write our new constant pool pages */
 
299
            const_mapper->write_to_image_file(writer, xor_mask);
 
300
 
 
301
            /* copy the EOF block to the new file unchanged */
 
302
            goto copy_block;
 
303
        }
 
304
        else
 
305
        {
 
306
        copy_block:
 
307
            /*
 
308
             *   For anything else, we'll simply copy the original block
 
309
             *   from the original image file unchanged. 
 
310
             */
 
311
            
 
312
            /* write the block header unchanged */
 
313
            newfp->write_bytes(buf, 10);
 
314
 
 
315
            /* copy the block in chunks */
 
316
            while (siz != 0)
 
317
            {
 
318
                size_t cur;
 
319
 
 
320
                /* 
 
321
                 *   read as much as we can, up to the amount remaining or
 
322
                 *   the buffer size, whichever is smaller 
 
323
                 */
 
324
                cur = sizeof(buf);
 
325
                if (cur > siz)
 
326
                    cur = (size_t)siz;
 
327
 
 
328
                /* read the data and write it out */
 
329
                origfp->read_bytes(buf, cur);
 
330
                newfp->write_bytes(buf, cur);
 
331
 
 
332
                /* deduct this chunk from the amount remaining */
 
333
                siz -= cur;
 
334
            }
 
335
        }
 
336
    }
 
337
 
 
338
    /* we're done with the image writer */
 
339
    delete writer;
 
340
 
 
341
    /* we're done with the constant mapper */
 
342
    delete const_mapper;
 
343
}
 
344
 
 
345
/* ------------------------------------------------------------------------ */
 
346
/*
 
347
 *   Object Table extension - rebuild the image file representations for
 
348
 *   all objects of a particular metaclass.
 
349
 */
 
350
void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
 
351
                                class CVmConstMapper *mapper)
 
352
{
 
353
    int pass;
 
354
    
 
355
    /* write persistent and transient objects separately */
 
356
    for (pass = 1 ; pass <= 2 ; ++pass)
 
357
    {
 
358
        int trans;
 
359
 
 
360
        /* write persistent on pass 1, transient on pass 2 */
 
361
        trans = (pass == 2);
 
362
 
 
363
        /* write the objects for this pass */
 
364
        rebuild_image(vmg_ meta_dep_idx, writer, mapper, trans);
 
365
    }
 
366
}
 
367
 
 
368
/*
 
369
 *   Write all of the transient or persistent objects of a given metaclass. 
 
370
 */
 
371
void CVmObjTable::rebuild_image(VMG_ int meta_dep_idx, CVmImageWriter *writer,
 
372
                                class CVmConstMapper *mapper, int trans)
 
373
{
 
374
    CVmObjPageEntry **pg;
 
375
    size_t i;
 
376
    vm_obj_id_t id;
 
377
    char *buf;
 
378
    ulong bufsiz;
 
379
    int block_cnt;
 
380
    ulong block_size;
 
381
    int large_objects;
 
382
 
 
383
    /* we haven't written anything to the file yet */
 
384
    block_cnt = 0;
 
385
    block_size = 0;
 
386
 
 
387
    /* presume we'll use small objects */
 
388
    large_objects = FALSE;
 
389
 
 
390
    /* 
 
391
     *   allocate an initial object buffer - we'll expand this as needed
 
392
     *   if we find an object that doesn't fit 
 
393
     */
 
394
    bufsiz = 4096;
 
395
    buf = (char *)t3malloc((size_t)bufsiz);
 
396
    if (buf == 0)
 
397
        err_throw(VMERR_OUT_OF_MEMORY);
 
398
 
 
399
    /* go through each page in the object table */
 
400
    for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
 
401
    {
 
402
        size_t j;
 
403
        CVmObjPageEntry *entry;
 
404
 
 
405
        /* start at the start of the page */
 
406
        j = VM_OBJ_PAGE_CNT;
 
407
        entry = *pg;
 
408
 
 
409
        /* go through each entry on this page */
 
410
        for ( ; j > 0 ; --j, ++entry, ++id)
 
411
        {
 
412
            /* 
 
413
             *   if this entry is in use, and its transient/persistent type
 
414
             *   matches the type we're writing, and its metaclass matches
 
415
             *   the one we're writing, write it out 
 
416
             */
 
417
            if (!entry->free_
 
418
                && (entry->transient_ != 0) == (trans != 0)
 
419
                && (G_meta_table->get_dependency_index(
 
420
                    entry->get_vm_obj()->get_metaclass_reg()->get_reg_idx())
 
421
                    == meta_dep_idx))
 
422
            {
 
423
                ulong objsiz;
 
424
 
 
425
                /* 
 
426
                 *   if this object has been mapped to a constant value,
 
427
                 *   there's no need to store it, since it is no longer
 
428
                 *   reachable 
 
429
                 */
 
430
                if (mapper->get_pool_addr(id) != 0)
 
431
                    continue;
 
432
 
 
433
                /* try building the object */
 
434
                objsiz = entry->get_vm_obj()->rebuild_image(vmg_ buf, bufsiz);
 
435
 
 
436
                /* if they need more space, reallocate the buffer */
 
437
                if (objsiz > bufsiz)
 
438
                {
 
439
                    /* if the object is too large, throw an error */
 
440
                    if (objsiz > OSMALMAX)
 
441
                        err_throw(VMERR_OBJ_SIZE_OVERFLOW);
 
442
                    
 
443
                    /* reallocate to next 4k increment */
 
444
                    bufsiz = (objsiz + 4095) & ~4095;
 
445
                    buf = (char *)t3realloc(buf, (size_t)bufsiz);
 
446
                    if (buf == 0)
 
447
                        err_throw(VMERR_OUT_OF_MEMORY);
 
448
 
 
449
                    /* try it again */
 
450
                    objsiz = entry->get_vm_obj()->
 
451
                             rebuild_image(vmg_ buf, bufsiz);
 
452
                }
 
453
 
 
454
                /* if the object is not empty, write it out */
 
455
                if (objsiz != 0)
 
456
                {
 
457
                    char prefix[20];
 
458
 
 
459
                    /* 
 
460
                     *   if this object's size exceeds 64k, and the
 
461
                     *   current OBJS block is a small block, end this
 
462
                     *   OBJS block and begin a large OBJS block 
 
463
                     */
 
464
                    if (objsiz > 65535 && !large_objects)
 
465
                    {
 
466
                        /* if we have a block open, end it */
 
467
                        if (block_cnt != 0)
 
468
                            writer->end_objs_block(block_cnt);
 
469
 
 
470
                        /* reset the count and size for the new block */
 
471
                        block_cnt = 0;
 
472
                        block_size = 0;
 
473
 
 
474
                        /* make the next block a large block */
 
475
                        large_objects = TRUE;
 
476
                    }
 
477
                    
 
478
                    /* 
 
479
                     *   if this object plus its prefix would push this
 
480
                     *   OBJS block over 64k, close it off and start a new
 
481
                     *   block 
 
482
                     */
 
483
                    if (block_size + objsiz + 6 > 64000L && block_cnt != 0)
 
484
                    {
 
485
                        /* close this block */
 
486
                        writer->end_objs_block(block_cnt);
 
487
 
 
488
                        /* reset the count and size for the new block */
 
489
                        block_cnt = 0;
 
490
                        block_size = 0;
 
491
 
 
492
                        /* 
 
493
                         *   use small size fields if this block isn't
 
494
                         *   itself large 
 
495
                         */
 
496
                        if (objsiz <= 65535)
 
497
                            large_objects = FALSE;
 
498
                    }
 
499
                    
 
500
                    /* if this is the first object, write the header */
 
501
                    if (block_cnt == 0)
 
502
                        writer->begin_objs_block(meta_dep_idx, large_objects,
 
503
                                                 trans);
 
504
 
 
505
                    /* write the prefix information */
 
506
                    oswp4(prefix, id);
 
507
                    if (large_objects)
 
508
                    {
 
509
                        /* write the 32-bit object size */
 
510
                        oswp4(prefix + 4, objsiz);
 
511
 
 
512
                        /* write the header */
 
513
                        writer->write_objs_bytes(prefix, 8);
 
514
                    }
 
515
                    else
 
516
                    {
 
517
                        /* write the 16-bit object size */
 
518
                        oswp2(prefix + 4, objsiz);
 
519
 
 
520
                        /* write the header */
 
521
                        writer->write_objs_bytes(prefix, 6);
 
522
                    }
 
523
 
 
524
                    /* write the object data */
 
525
                    writer->write_objs_bytes(buf, objsiz);
 
526
 
 
527
                    /* count the object */
 
528
                    ++block_cnt;
 
529
 
 
530
                    /* count the size (including the prefix) */
 
531
                    block_size += objsiz + 6;
 
532
                }
 
533
            }
 
534
        }
 
535
    }
 
536
 
 
537
    /* if we wrote any objects, end the OBJS block */
 
538
    if (block_cnt != 0)
 
539
        writer->end_objs_block(block_cnt);
 
540
 
 
541
    /* delete our buffer */
 
542
    t3free(buf);
 
543
}
 
544
 
 
545
/*
 
546
 *   Scan all active objects and convert objects to constant data where
 
547
 *   possible.  Certain object metaclasses, such as strings and lists, can
 
548
 *   be represented in a rebuilt image file as constant data; this routine
 
549
 *   makes all of these conversions.  
 
550
 */
 
551
void CVmObjTable::rebuild_image_convert_const_data
 
552
   (VMG_ CVmConstMapper *const_mapper)
 
553
{
 
554
    CVmObjPageEntry **pg;
 
555
    size_t i;
 
556
    vm_obj_id_t id;
 
557
 
 
558
    /* 
 
559
     *   First pass: go through each page in the object table, and reserve
 
560
     *   space for the constant conversion.  This assigns each object that
 
561
     *   can be converted its address in the new constant pool pages. 
 
562
     */
 
563
    for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
 
564
    {
 
565
        size_t j;
 
566
        CVmObjPageEntry *entry;
 
567
 
 
568
        /* start at the start of the page, but skip object ID = 0 */
 
569
        j = VM_OBJ_PAGE_CNT;
 
570
        entry = *pg;
 
571
 
 
572
        /* go through each entry on this page */
 
573
        for ( ; j > 0 ; --j, ++entry, ++id)
 
574
        {
 
575
            /* 
 
576
             *   if this entry is in use, tell it to reserve space for
 
577
             *   conversion to constant data 
 
578
             */
 
579
            if (!entry->free_)
 
580
                entry->get_vm_obj()
 
581
                    ->reserve_const_data(vmg_ const_mapper, id);
 
582
        }
 
583
    }
 
584
 
 
585
    /* prepare the constant mapper to begin storing */
 
586
    const_mapper->prepare_to_store_data();
 
587
 
 
588
    /*
 
589
     *   Second pass: go through the objects once again and make the
 
590
     *   conversions.  We must do this on a separate second pass because
 
591
     *   we must fix up all references to objects being converted, hence
 
592
     *   we must know the conversion status of every object to be
 
593
     *   converted before we can fix up anything.  
 
594
     */
 
595
    for (id = 0, i = pages_used_, pg = pages_ ; i > 0 ; ++pg, --i)
 
596
    {
 
597
        size_t j;
 
598
        CVmObjPageEntry *entry;
 
599
 
 
600
        /* start at the start of the page, but skip object ID = 0 */
 
601
        j = VM_OBJ_PAGE_CNT;
 
602
        entry = *pg;
 
603
 
 
604
        /* go through each entry on this page */
 
605
        for ( ; j > 0 ; --j, ++entry, ++id)
 
606
        {
 
607
            /* if this entry is in use, convert it if possible */
 
608
            if (!entry->free_)
 
609
                entry->get_vm_obj()
 
610
                    ->convert_to_const_data(vmg_ const_mapper, id);
 
611
        }
 
612
    }
 
613
}
 
614
 
 
615
/* ------------------------------------------------------------------------ */
 
616
/*
 
617
 *   Convert a value to constant data.  If the value is an object that has
 
618
 *   reserved constant data space for itself, we'll update the value to
 
619
 *   reflect the constant data conversion for the value.  
 
620
 */
 
621
static void convert_dh_to_const_data(VMG_ CVmConstMapper *mapper, char *p)
 
622
{
 
623
    /* if the value is an object, check for conversion */
 
624
    if (vmb_get_dh_type(p) == VM_OBJ)
 
625
    {
 
626
        vm_obj_id_t obj;
 
627
        ulong addr;
 
628
 
 
629
        /* get the object value */
 
630
        obj = vmb_get_dh_obj(p);
 
631
 
 
632
        /* look up the object's converted constant pool address */
 
633
        if ((addr = mapper->get_pool_addr(obj)) != 0)
 
634
        {
 
635
            vm_val_t val;
 
636
 
 
637
            /* this value has been converted - set the new value */
 
638
            val.typ = vm_objp(vmg_ obj)->get_convert_to_const_data_type();
 
639
            val.val.ofs = addr;
 
640
            vmb_put_dh(p, &val);
 
641
        }
 
642
    }
 
643
}
 
644
 
 
645
/*
 
646
 *   Convert a vm_val_t value to constant data if appropriate. 
 
647
 */
 
648
static void convert_val_to_const_data(VMG_ CVmConstMapper *mapper,
 
649
                                      vm_val_t *val)
 
650
{
 
651
    /* if it's an object, check for conversion */
 
652
    if (val->typ == VM_OBJ)
 
653
    {
 
654
        ulong addr;
 
655
 
 
656
        /* look up the object's converted constant pool address */
 
657
        if ((addr = mapper->get_pool_addr(val->val.obj)) != 0)
 
658
        {
 
659
            /* this value has been converted - set the new value */
 
660
            val->typ = vm_objp(vmg_ val->val.obj)
 
661
                       ->get_convert_to_const_data_type();
 
662
            val->val.ofs = addr;
 
663
        }
 
664
    }
 
665
}
 
666
 
 
667
/* ------------------------------------------------------------------------ */
 
668
/*
 
669
 *   List Metaclass implementation - image rebuilding 
 
670
 */
 
671
 
 
672
/*
 
673
 *   Build an image file record for the list.  We need this because we
 
674
 *   can't always convert a list to a constant value when rebuilding an
 
675
 *   image file; in particular, if the list exceeds the size of a constant
 
676
 *   pool page (unlikely but possible), we will have to store the list as
 
677
 *   object data.  
 
678
 */
 
679
ulong CVmObjList::rebuild_image(VMG_ char *buf, ulong buflen)
 
680
{
 
681
    size_t copy_size;
 
682
 
 
683
    /* calculate how much space we need to store the data */
 
684
    copy_size = calc_alloc(vmb_get_len(ext_));
 
685
    
 
686
    /* make sure we have room for our data */
 
687
    if (copy_size > buflen)
 
688
        return copy_size;
 
689
 
 
690
    /* copy the data */
 
691
    memcpy(buf, ext_, copy_size);
 
692
 
 
693
    /* return the size */
 
694
    return copy_size;
 
695
}
 
696
 
 
697
/*
 
698
 *   Reserve space in a rebuilt constant pool for converting our data to a
 
699
 *   constant value.
 
700
 */
 
701
void CVmObjList::reserve_const_data(VMG_ CVmConstMapper *mapper,
 
702
                                    vm_obj_id_t self)
 
703
{
 
704
    /* reserve the space for our list data */
 
705
    mapper->alloc_pool_space(self, calc_alloc(vmb_get_len(ext_)));
 
706
}
 
707
 
 
708
 
 
709
/*
 
710
 *   Convert to constant data.  We must check each object that we
 
711
 *   reference via a modified property and convert it to a constant data
 
712
 *   item if necessary.  Then, we must store our data in the constant
 
713
 *   pool, if we successfully reserved an address for it.  
 
714
 */
 
715
void CVmObjList::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
716
                                       vm_obj_id_t self)
 
717
{
 
718
    size_t cnt;
 
719
    char *p;
 
720
 
 
721
    /* get my element count */
 
722
    cnt = vmb_get_len(ext_);
 
723
 
 
724
    /* mark as referenced each object in our list */
 
725
    for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
 
726
    {
 
727
        /* convert the value to constant data if possible */
 
728
        convert_dh_to_const_data(vmg_ mapper, p);
 
729
    }
 
730
 
 
731
    /* 
 
732
     *   if we managed to convert our object to constant data, store our
 
733
     *   value 
 
734
     */
 
735
    if (mapper->get_pool_addr(self))
 
736
        mapper->store_data(self, ext_, calc_alloc(vmb_get_len(ext_)));
 
737
}
 
738
 
 
739
 
 
740
/* ------------------------------------------------------------------------ */
 
741
/*
 
742
 *   String Metaclass implementation - image rebuilding 
 
743
 */
 
744
 
 
745
/*
 
746
 *   Build an image file record for the string.  We need this because we
 
747
 *   can't always convert a string to a constant value when rebuilding an
 
748
 *   image file; in particular, if the string exceeds the size of a
 
749
 *   constant pool page (unlikely but possible), we will have to store the
 
750
 *   string as object data.  
 
751
 */
 
752
ulong CVmObjString::rebuild_image(VMG_ char *buf, ulong buflen)
 
753
{
 
754
    size_t copy_size;
 
755
 
 
756
    /* calculate how much space we need to store the data */
 
757
    copy_size = vmb_get_len(ext_) + VMB_LEN;
 
758
 
 
759
    /* make sure we have room for our data */
 
760
    if (copy_size > buflen)
 
761
        return copy_size;
 
762
 
 
763
    /* copy the data */
 
764
    memcpy(buf, ext_, copy_size);
 
765
 
 
766
    /* return the size */
 
767
    return copy_size;
 
768
}
 
769
 
 
770
/*
 
771
 *   Reserve space in a rebuilt constant pool for converting our data to a
 
772
 *   constant value.
 
773
 */
 
774
void CVmObjString::reserve_const_data(VMG_ CVmConstMapper *mapper,
 
775
                                      vm_obj_id_t self)
 
776
{
 
777
    /* reserve the space for our string data */
 
778
    mapper->alloc_pool_space(self, vmb_get_len(ext_) + VMB_LEN);
 
779
}
 
780
 
 
781
 
 
782
/*
 
783
 *   Convert to constant data.  We don't reference any objects, so we need
 
784
 *   simply store our data in the constant pool, if we successfully
 
785
 *   reserved an address for it.  
 
786
 */
 
787
void CVmObjString::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
788
                                         vm_obj_id_t self)
 
789
{
 
790
    /* 
 
791
     *   if we managed to convert our object to constant data, store our
 
792
     *   value 
 
793
     */
 
794
    if (mapper->get_pool_addr(self))
 
795
        mapper->store_data(self, ext_, vmb_get_len(ext_) + VMB_LEN);
 
796
}
 
797
 
 
798
 
 
799
/* ------------------------------------------------------------------------ */
 
800
/*
 
801
 *   TADS Object implementation - image rebuilding 
 
802
 */
 
803
 
 
804
 
 
805
/* property comparison callback for qsort() */
 
806
extern "C"
 
807
{
 
808
    static int prop_compare(const void *p1, const void *p2)
 
809
    {
 
810
        uint id1, id2;
 
811
 
 
812
        /* get the ID's */
 
813
        id1 = osrp2(p1);
 
814
        id2 = osrp2(p2);
 
815
 
 
816
        /* compare them and return the result */
 
817
        return (id1 < id2 ? -1 : id1 == id2 ? 0 : 1);
 
818
    }
 
819
}
 
820
 
 
821
/*
 
822
 *   build an image file record for the object 
 
823
 */
 
824
ulong CVmObjTads::rebuild_image(VMG_ char *buf, ulong buflen)
 
825
{
 
826
    size_t max_size;
 
827
    char *p;
 
828
    char *props;
 
829
    char *props_end;
 
830
    size_t prop_cnt;
 
831
    size_t i;
 
832
    vm_tadsobj_prop *entry;
 
833
    
 
834
    /* 
 
835
     *   Make sure the buffer is big enough.  Start out with worst-case
 
836
     *   assumption that we'll need every allocated property slot; we might
 
837
     *   actually need fewer, since some of the slots could be empty by
 
838
     *   virtue of having been undone.  We need space for our own header
 
839
     *   (UINT2 superclass count, UINT2 property count, UINT2 flags), plus a
 
840
     *   UINT4 per superclass, plus a (UINT2 + DATAHOLDER) per property.  
 
841
     */
 
842
    max_size = (2 + 2 + 2)
 
843
               + get_sc_count() * 4
 
844
               + (get_hdr()->prop_entry_free * 7);
 
845
 
 
846
    /* if it's more than we have available, ask for more space */
 
847
    if (max_size > buflen)
 
848
        return max_size;
 
849
 
 
850
    /* 
 
851
     *   set up our header - use a placeholder 0 for the property count
 
852
     *   for now, until we calculate the real value 
 
853
     */
 
854
    oswp2(buf, get_sc_count());
 
855
    oswp2(buf+2, 0);
 
856
    oswp2(buf+4, get_li_obj_flags());
 
857
    p = buf + 6;
 
858
 
 
859
    /* copy the superclass list */
 
860
    for (i = 0 ; i < get_sc_count() ; ++i, p += 4)
 
861
        oswp4(p, get_sc(i));
 
862
 
 
863
    /* remember where the properties start */
 
864
    props = p;
 
865
 
 
866
    /* copy the non-empty property slots */
 
867
    for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr,
 
868
         prop_cnt = 0 ;
 
869
         i != 0 ;
 
870
         --i, ++entry)
 
871
    {
 
872
        /* if this slot is non-empty, store it */
 
873
        if (entry->val.typ != VM_EMPTY)
 
874
        {
 
875
            /* set up this slot */
 
876
            oswp2(p, entry->prop);
 
877
            vmb_put_dh(p + 2, &entry->val);
 
878
 
 
879
            /* count the additional property */
 
880
            ++prop_cnt;
 
881
 
 
882
            /* move past it in the buffer */
 
883
            p += 7;
 
884
        }
 
885
    }
 
886
 
 
887
    /* remember where the properties end */
 
888
    props_end = p;
 
889
 
 
890
    /* fill in actual the property count now that we know it */
 
891
    oswp2(buf+2, prop_cnt);
 
892
 
 
893
    /* sort the new table */
 
894
    qsort(props, prop_cnt, 7, &prop_compare);
 
895
 
 
896
    /* return the final size */
 
897
    return p - buf;
 
898
}
 
899
 
 
900
/*
 
901
 *   Convert to constant data.  We must check each object that we
 
902
 *   reference via a modified property and convert it to a constant data
 
903
 *   item if necessary.  
 
904
 */
 
905
void CVmObjTads::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
906
                                       vm_obj_id_t /*self*/)
 
907
{
 
908
    size_t i;
 
909
    vm_tadsobj_prop *entry;
 
910
 
 
911
    /* 
 
912
     *   Scan the property entries.  Note that we don't have to worry about
 
913
     *   the original properties, since they can only refer to data that was
 
914
     *   in the original image file, and hence never need to be converted --
 
915
     *   if it was good enough for the original image file, it's good enough
 
916
     *   for us.  
 
917
     */
 
918
    for (i = get_hdr()->prop_entry_free, entry = get_hdr()->prop_entry_arr ;
 
919
         i != 0 ; --i, ++entry)
 
920
    {
 
921
        /* if this slot is modified, convert it */
 
922
        if ((entry->flags & VMTO_PROP_MOD) != 0
 
923
            && entry->val.typ != VM_EMPTY)
 
924
        {
 
925
            /* convert the value */
 
926
            convert_val_to_const_data(vmg_ mapper, &entry->val);
 
927
        }
 
928
    }
 
929
}
 
930
 
 
931
 
 
932
/* ------------------------------------------------------------------------ */
 
933
/*
 
934
 *   Dictionary object implementation - image rebuilding 
 
935
 */
 
936
 
 
937
/* 
 
938
 *   callback context for image rebuild 
 
939
 */
 
940
struct rebuild_ctx
 
941
{
 
942
    /* space needed so far */
 
943
    size_t space_needed;
 
944
 
 
945
    /* number of entries */
 
946
    size_t entry_cnt;
 
947
 
 
948
    /* next available output pointer */
 
949
    char *dst;
 
950
};
 
951
 
 
952
/* 
 
953
 *   rebuild for image file 
 
954
 */
 
955
ulong CVmObjDict::rebuild_image(VMG_ char *buf, ulong buflen)
 
956
{
 
957
    rebuild_ctx ctx;
 
958
    
 
959
    /* 
 
960
     *   calculate the amount of space we need - start with the comparator
 
961
     *   object, which needs four bytes, and the entry count, which needs two
 
962
     *   bytes 
 
963
     */
 
964
    ctx.space_needed = 6;
 
965
 
 
966
    /* enumerate the entries to count space */
 
967
    ctx.entry_cnt = 0;
 
968
    get_ext()->hashtab_->enum_entries(&rebuild_cb_1, &ctx);
 
969
 
 
970
    /* if we need more space than is available, ask for more */
 
971
    if (ctx.space_needed > buflen)
 
972
        return ctx.space_needed;
 
973
 
 
974
    /* write the comparator object and the entry count */
 
975
    oswp4(buf, get_ext()->comparator_);
 
976
    oswp2(buf + 4, ctx.entry_cnt);
 
977
 
 
978
    /* start writing after the count */
 
979
    ctx.dst = buf + 6;
 
980
 
 
981
    /* enumerate the entries to write to the image buffer */
 
982
    get_ext()->hashtab_->enum_entries(&rebuild_cb_2, &ctx);
 
983
 
 
984
    /* return the size */
 
985
    return ctx.dst - buf;
 
986
}
 
987
 
 
988
/*
 
989
 *   enumeration callback - rebuild phase 1: count the space needed for
 
990
 *   this table entry 
 
991
 */
 
992
void CVmObjDict::rebuild_cb_1(void *ctx0, class CVmHashEntry *entry0)
 
993
{
 
994
    rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
 
995
    CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
 
996
    vm_dict_entry *cur;
 
997
 
 
998
    /* 
 
999
     *   count the space needed for this string - one byte for the length
 
1000
     *   prefix, the bytes of the name string, and two bytes for the item
 
1001
     *   count 
 
1002
     */
 
1003
    ctx->space_needed += 1 + entry->getlen() + 2;
 
1004
 
 
1005
    /* count this entry */
 
1006
    ++ctx->entry_cnt;
 
1007
 
 
1008
    /* count the items */
 
1009
    for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
 
1010
    {
 
1011
        /* 
 
1012
         *   add the space for this item - four byte for the object ID,
 
1013
         *   two bytes for the property ID 
 
1014
         */
 
1015
        ctx->space_needed += 6;
 
1016
    }
 
1017
}
 
1018
 
 
1019
/*
 
1020
 *   enumeration callback - rebuild phase 2: write the entries to the
 
1021
 *   image buffer 
 
1022
 */
 
1023
void CVmObjDict::rebuild_cb_2(void *ctx0, class CVmHashEntry *entry0)
 
1024
{
 
1025
    rebuild_ctx *ctx = (rebuild_ctx *)ctx0;
 
1026
    CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
 
1027
    vm_dict_entry *cur;
 
1028
    size_t cnt;
 
1029
    char *p;
 
1030
    size_t rem;
 
1031
 
 
1032
    /* count the entries in our list */
 
1033
    for (cnt = 0, cur = entry->get_head() ; cur != 0 ;
 
1034
         cur = cur->nxt_, ++cnt) ;
 
1035
 
 
1036
    /* write the length of the key string followed by the key string */
 
1037
    *ctx->dst++ = (char)entry->getlen();
 
1038
    memcpy(ctx->dst, entry->getstr(), entry->getlen());
 
1039
 
 
1040
    /* xor the key string's bytes */
 
1041
    for (p = ctx->dst, rem = entry->getlen() ; rem != 0 ;
 
1042
         --rem, ++p)
 
1043
        *p ^= 0xBD;
 
1044
 
 
1045
    /* move past the key string */
 
1046
    ctx->dst += entry->getlen();
 
1047
 
 
1048
    /* write the item count */
 
1049
    oswp2(ctx->dst, cnt);
 
1050
    ctx->dst += 2;
 
1051
 
 
1052
    /* write the items */
 
1053
    for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
 
1054
    {
 
1055
        /* write the object ID and property ID for this item */
 
1056
        oswp4(ctx->dst, (ulong)cur->obj_);
 
1057
        oswp2(ctx->dst + 4, (uint)cur->prop_);
 
1058
        ctx->dst += 6;
 
1059
    }
 
1060
}
 
1061
 
 
1062
/*
 
1063
 *   callback context for constant data conversion 
 
1064
 */
 
1065
struct cvt_const_ctx
 
1066
{
 
1067
    /* constant mapper */
 
1068
    CVmConstMapper *mapper;
 
1069
};
 
1070
 
 
1071
/* 
 
1072
 *   convert to constant data 
 
1073
 */
 
1074
void CVmObjDict::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
1075
                                       vm_obj_id_t self)
 
1076
{
 
1077
    cvt_const_ctx ctx;
 
1078
    
 
1079
    /* make sure the comparator object isn't mappable to a constant */
 
1080
    if (mapper->get_pool_addr(get_ext()->comparator_) != 0)
 
1081
        err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
 
1082
                    "<comparator>", 12);
 
1083
 
 
1084
    /* 
 
1085
     *   Go through our dictionary and make sure we don't have any
 
1086
     *   references to constant data.  We don't actually have to perform
 
1087
     *   any conversions, because we simply don't allow references to
 
1088
     *   anything but TADS-object objects in the dictionary.  
 
1089
     */
 
1090
    ctx.mapper = mapper;
 
1091
    get_ext()->hashtab_->enum_entries(&cvt_const_cb, &ctx);
 
1092
}
 
1093
 
 
1094
/*
 
1095
 *   enumeration callback - convert to constant data 
 
1096
 */
 
1097
void CVmObjDict::cvt_const_cb(void *ctx0, class CVmHashEntry *entry0)
 
1098
{
 
1099
    cvt_const_ctx *ctx = (cvt_const_ctx *)ctx0;
 
1100
    CVmHashEntryDict *entry = (CVmHashEntryDict *)entry0;
 
1101
    vm_dict_entry *cur;
 
1102
 
 
1103
    /* inspect the items in this entry's list */
 
1104
    for (cur = entry->get_head() ; cur != 0 ; cur = cur->nxt_)
 
1105
    {
 
1106
        /* if this item refers to constant data, it's an error */
 
1107
        if (ctx->mapper->get_pool_addr(cur->obj_) != 0)
 
1108
            err_throw_a(VMERR_DICT_NO_CONST, 1, ERR_TYPE_TEXTCHAR_LEN,
 
1109
                        entry->getstr(), entry->getlen());
 
1110
    }
 
1111
}
 
1112
 
 
1113
/* ------------------------------------------------------------------------ */
 
1114
/*
 
1115
 *   Grammar production object - image rebuilding operations 
 
1116
 */
 
1117
 
 
1118
/* 
 
1119
 *   rebuild for image file - we can't change at run-time, so we must
 
1120
 *   simply copy our image file data back out unchanged 
 
1121
 */
 
1122
ulong CVmObjGramProd::rebuild_image(VMG_ char *buf, ulong buflen)
 
1123
{
 
1124
    /* make sure we have room */
 
1125
    if (get_ext()->image_data_size_ > buflen)
 
1126
        return get_ext()->image_data_size_;
 
1127
 
 
1128
    /* copy the data */
 
1129
    memcpy(buf, get_ext()->image_data_, get_ext()->image_data_size_);
 
1130
 
 
1131
    /* return the size */
 
1132
    return get_ext()->image_data_size_;
 
1133
}
 
1134
 
 
1135
/* ------------------------------------------------------------------------ */
 
1136
/*
 
1137
 *   BigNumber Metaclass implementation - image rebuilding 
 
1138
 */
 
1139
 
 
1140
/*
 
1141
 *   Build an image file record
 
1142
 */
 
1143
ulong CVmObjBigNum::rebuild_image(VMG_ char *buf, ulong buflen)
 
1144
{
 
1145
    size_t copy_size;
 
1146
 
 
1147
    /* calculate how much space we need to store the data */
 
1148
    copy_size = calc_alloc(get_prec(ext_));
 
1149
 
 
1150
    /* make sure we have room for our data */
 
1151
    if (copy_size > buflen)
 
1152
        return copy_size;
 
1153
 
 
1154
    /* copy the data */
 
1155
    memcpy(buf, ext_, copy_size);
 
1156
 
 
1157
    /* return the size */
 
1158
    return copy_size;
 
1159
}
 
1160
 
 
1161
/*
 
1162
 *   Reserve space in a rebuilt constant pool for converting our data to a
 
1163
 *   constant value.
 
1164
 */
 
1165
void CVmObjBigNum::reserve_const_data(VMG_ CVmConstMapper *mapper,
 
1166
                                      vm_obj_id_t self)
 
1167
{
 
1168
    /* BigNumber values cannot be converted to constants */
 
1169
}
 
1170
 
 
1171
 
 
1172
/*
 
1173
 *   Convert to constant data.  We don't reference any other objects, so
 
1174
 *   we simply need to store our data in the constant pool, if we
 
1175
 *   successfully reserved an address for it.  
 
1176
 */
 
1177
void CVmObjBigNum::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
1178
                                         vm_obj_id_t self)
 
1179
{
 
1180
    /* 
 
1181
     *   if we managed to convert our object to constant data, store our
 
1182
     *   value 
 
1183
     */
 
1184
    if (mapper->get_pool_addr(self))
 
1185
        mapper->store_data(self, ext_, calc_alloc(get_prec(ext_)));
 
1186
}
 
1187
 
 
1188
 
 
1189
/* ------------------------------------------------------------------------ */
 
1190
/*
 
1191
 *   Object-to-Constant mapper 
 
1192
 */
 
1193
 
 
1194
/*
 
1195
 *   initialize 
 
1196
 */
 
1197
CVmConstMapper::CVmConstMapper(VMG0_)
 
1198
{
 
1199
    vm_obj_id_t max_id;
 
1200
    size_t i;
 
1201
    
 
1202
    /* get the maximum object ID we've allocated */
 
1203
    max_id = G_obj_table->get_max_used_obj_id();
 
1204
 
 
1205
    /* 
 
1206
     *   Allocate our master translation page list so that we have room
 
1207
     *   for enough pages to store the maximum object ID.  We need one
 
1208
     *   slot for each page of 1024 translation elements.  
 
1209
     */
 
1210
    obj_addr_cnt_ = max_id / 1024;
 
1211
    obj_addr_ = (ulong **)t3malloc(obj_addr_cnt_ * sizeof(obj_addr_[0]));
 
1212
    if (obj_addr_ == 0)
 
1213
        err_throw(VMERR_OUT_OF_MEMORY);
 
1214
 
 
1215
    /* clear out the page slots - we haven't allocated any pages yet */
 
1216
    for (i = 0 ; i < obj_addr_cnt_ ; ++i)
 
1217
        obj_addr_[i] = 0;
 
1218
 
 
1219
    /* get the constant pool's page size */
 
1220
    page_size_ = G_const_pool->get_page_size();
 
1221
 
 
1222
    /* 
 
1223
     *   our first page is the next page after the last page in the
 
1224
     *   current pool 
 
1225
     */
 
1226
    first_page_idx_ = G_const_pool->get_page_count();
 
1227
 
 
1228
    /* 
 
1229
     *   get the starting address - we'll start writing our data at the
 
1230
     *   first page after all existing pages in the pool 
 
1231
     */
 
1232
    base_addr_ = page_size_ * G_const_pool->get_page_count();
 
1233
 
 
1234
    /* allocate from our base address */
 
1235
    next_free_ = base_addr_;
 
1236
 
 
1237
    /* we have the entire first page available */
 
1238
    rem_ = page_size_;
 
1239
 
 
1240
    /* 
 
1241
     *   we haven't allocated any page data yet (we'll do this after we've
 
1242
     *   reserved all space, when the client invokes
 
1243
     *   prepare_to_store_data()) 
 
1244
     */
 
1245
    pages_ = 0;
 
1246
    pages_cnt_ = 0;
 
1247
}
 
1248
 
 
1249
/*
 
1250
 *   delete 
 
1251
 */
 
1252
CVmConstMapper::~CVmConstMapper()
 
1253
{
 
1254
    size_t i;
 
1255
    
 
1256
    /* delete each page in our mapping table */
 
1257
    for (i = 0 ; i < obj_addr_cnt_ ; ++i)
 
1258
    {
 
1259
        /* free this page if we allocated it */
 
1260
        if (obj_addr_[i] != 0)
 
1261
            t3free(obj_addr_[i]);
 
1262
    }
 
1263
 
 
1264
    /* delete our mapping table */
 
1265
    t3free(obj_addr_);
 
1266
 
 
1267
    /* delete each constant pool page */
 
1268
    for (i = 0 ; i < pages_cnt_ ; ++i)
 
1269
        t3free(pages_[i]);
 
1270
 
 
1271
    /* free the page list */
 
1272
    if (pages_ != 0)
 
1273
        t3free(pages_);
 
1274
}
 
1275
 
 
1276
/*
 
1277
 *   Get an object's pool address, if it has one 
 
1278
 */
 
1279
ulong CVmConstMapper::get_pool_addr(vm_obj_id_t obj_id)
 
1280
{
 
1281
    /* determine if the page is mapped - if not, the object isn't mapped */
 
1282
    if (obj_addr_[obj_id / 1024] == 0)
 
1283
        return 0;
 
1284
 
 
1285
    /* return the mapping entry */
 
1286
    return obj_addr_[obj_id / 1024][obj_id % 1024];
 
1287
}
 
1288
 
 
1289
 
 
1290
/*
 
1291
 *   Allocate space in the pool for an object's data
 
1292
 */
 
1293
ulong CVmConstMapper::alloc_pool_space(vm_obj_id_t obj_id, size_t len)
 
1294
{
 
1295
    ulong ret;
 
1296
    
 
1297
    /* 
 
1298
     *   if the data block is too large to fit on a constant pool page, we
 
1299
     *   can't store it 
 
1300
     */
 
1301
    if (len > page_size_)
 
1302
        return 0;
 
1303
    
 
1304
    /* if the translation page isn't mapped yet, map it */
 
1305
    if (obj_addr_[obj_id / 1024] == 0)
 
1306
    {
 
1307
        /* allocate a new page */
 
1308
        obj_addr_[obj_id / 1024] =
 
1309
            (ulong *)t3malloc(1024 * sizeof(obj_addr_[0][0]));
 
1310
 
 
1311
        /* if that failed, throw an error */
 
1312
        if (obj_addr_[obj_id / 1024] == 0)
 
1313
            err_throw(VMERR_OUT_OF_MEMORY);
 
1314
 
 
1315
        /* clear the new page */
 
1316
        memset(obj_addr_[obj_id / 1024], 0, 1024 * sizeof(obj_addr_[0][0]));
 
1317
    }
 
1318
 
 
1319
    /* if the block doesn't fit on this page, skip to the next page */
 
1320
    if (len > rem_)
 
1321
    {
 
1322
        /* skip past the remainder of this page */
 
1323
        next_free_ += rem_;
 
1324
 
 
1325
        /* we have the whole next page available now */
 
1326
        rem_ = page_size_;
 
1327
    }
 
1328
 
 
1329
    /* remember the object ID's address in the translation list */
 
1330
    ret = obj_addr_[obj_id / 1024][obj_id % 1024] = next_free_;
 
1331
 
 
1332
    /* skip past the data */
 
1333
    next_free_ += len;
 
1334
    rem_ -= len;
 
1335
 
 
1336
    /* return the new address */
 
1337
    return ret;
 
1338
}
 
1339
 
 
1340
/*
 
1341
 *   Prepare to begin storing data 
 
1342
 */
 
1343
void CVmConstMapper::prepare_to_store_data()
 
1344
{
 
1345
    size_t i;
 
1346
    
 
1347
    /* figure out how many pages we need */
 
1348
    pages_cnt_ = calc_page_count();
 
1349
 
 
1350
    /* allocate space for the list of pages */
 
1351
    pages_ = (vm_const_mapper_page **)
 
1352
             t3malloc(pages_cnt_ * sizeof(pages_[0]));
 
1353
    if (pages_ == 0)
 
1354
        err_throw(VMERR_OUT_OF_MEMORY);
 
1355
 
 
1356
    /* allocate each page */
 
1357
    for (i = 0 ; i < pages_cnt_ ; ++i)
 
1358
    {
 
1359
        /* 
 
1360
         *   allocate this page - allocate the header structure plus space
 
1361
         *   for the page data 
 
1362
         */
 
1363
        pages_[i] = (vm_const_mapper_page *)
 
1364
                    t3malloc(sizeof(pages_[i]) + page_size_);
 
1365
        if (pages_[i] == 0)
 
1366
            err_throw(VMERR_OUT_OF_MEMORY);
 
1367
 
 
1368
        /* the page has nothing stored yet */
 
1369
        pages_[i]->max_ofs_used = 0;
 
1370
    }
 
1371
}
 
1372
 
 
1373
/*
 
1374
 *   Store an object's data 
 
1375
 */
 
1376
void CVmConstMapper::store_data(vm_obj_id_t obj_id,
 
1377
                                const void *ptr, size_t len)
 
1378
{
 
1379
    ulong addr;
 
1380
    size_t page_idx;
 
1381
    size_t page_ofs;
 
1382
 
 
1383
    /* get the pool address that was reserved for the object */
 
1384
    addr = get_pool_addr(obj_id);
 
1385
 
 
1386
    /* if the object had no space reserved, ignore the request */
 
1387
    if (addr == 0)
 
1388
        return;
 
1389
 
 
1390
    /* figure out which page this address is in, and the offset in the page */
 
1391
    page_idx = (size_t)((addr - base_addr_) / page_size_);
 
1392
    page_ofs = (size_t)((addr - base_addr_) % page_size_);
 
1393
 
 
1394
    /* 
 
1395
     *   if this address takes us above the high-water mark for the page,
 
1396
     *   move the page's marker accordingly 
 
1397
     */
 
1398
    if (page_ofs + len > pages_[page_idx]->max_ofs_used)
 
1399
        pages_[page_idx]->max_ofs_used = page_ofs + len;
 
1400
 
 
1401
    /* copy the data */
 
1402
    memcpy(pages_[page_idx]->buf + page_ofs, ptr, len);
 
1403
}
 
1404
 
 
1405
/*
 
1406
 *   Write the pool pages to an image file 
 
1407
 */
 
1408
void CVmConstMapper::write_to_image_file(CVmImageWriter *writer,
 
1409
                                         uchar xor_mask)
 
1410
{
 
1411
    size_t i;
 
1412
    
 
1413
    /* go through each of our pages */
 
1414
    for (i = 0 ; i < pages_cnt_ ; ++i)
 
1415
    {
 
1416
        /* write this page - it's in pool 2 (the constant data pool) */
 
1417
        writer->write_pool_page(2, first_page_idx_ + i,
 
1418
                                pages_[i]->buf, pages_[i]->max_ofs_used,
 
1419
                                TRUE, xor_mask);
 
1420
    }
 
1421
}
 
1422
 
 
1423
/* ------------------------------------------------------------------------ */
 
1424
/*
 
1425
 *   metaclass table - image rewriter implementation 
 
1426
 */
 
1427
 
 
1428
/*
 
1429
 *   write the new metaclass dependency table 
 
1430
 */
 
1431
void CVmMetaTable::rebuild_image(CVmImageWriter *writer)
 
1432
{
 
1433
    size_t i;
 
1434
    
 
1435
    /* begin the new metaclass dependency table */
 
1436
    writer->begin_meta_dep(get_count());
 
1437
 
 
1438
    /* write the new metaclass dependency table items */
 
1439
    for (i = 0 ; i < get_count() ; ++i)
 
1440
    {
 
1441
        vm_meta_entry_t *entry;
 
1442
        ushort j;
 
1443
 
 
1444
        /* get this entry */
 
1445
        entry = get_entry(i);
 
1446
        
 
1447
        /* write this metaclass name */
 
1448
        writer->write_meta_dep_item(entry->image_meta_name_);
 
1449
 
 
1450
        /* 
 
1451
         *   Write the property translation list.  Note that xlat_func()
 
1452
         *   requires a 1-based index, so we loop from 1 to the count,
 
1453
         *   rather than following the usual C-style 0-based conventions.  
 
1454
         */
 
1455
        for (j = 1 ; j <= entry->func_xlat_cnt_ ; ++j)
 
1456
            writer->write_meta_item_prop(entry->xlat_func(j));
 
1457
    }
 
1458
    
 
1459
    /* end the metaclass dependency table */
 
1460
    writer->end_meta_dep();
 
1461
}
 
1462
 
 
1463
 
 
1464
/* ------------------------------------------------------------------------ */
 
1465
/*
 
1466
 *   Hashtable Metaclass implementation - image rebuilding 
 
1467
 */
 
1468
 
 
1469
/*
 
1470
 *   Build an image file record 
 
1471
 */
 
1472
ulong CVmObjLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
 
1473
{
 
1474
    size_t copy_size;
 
1475
    vm_lookup_ext *ext = get_ext();
 
1476
    uint i;
 
1477
    vm_lookup_val **bp;
 
1478
    vm_lookup_val *val;
 
1479
    char *dst;
 
1480
    uint idx;
 
1481
 
 
1482
    /* 
 
1483
     *   we need space for the fixed header (6 bytes), 2 bytes per bucket,
 
1484
     *   and the entries themselves 
 
1485
     */
 
1486
    copy_size = 6
 
1487
                + (ext->bucket_cnt * 2)
 
1488
                + (ext->value_cnt * VMLOOKUP_VALUE_SIZE);
 
1489
 
 
1490
    /* make sure we have room for our data */
 
1491
    if (copy_size > buflen)
 
1492
        return copy_size;
 
1493
 
 
1494
    /* write the fixed data */
 
1495
    oswp2(buf, ext->bucket_cnt);
 
1496
    oswp2(buf + 2, ext->value_cnt);
 
1497
    idx = ext->val_to_img_idx(ext->first_free);
 
1498
    oswp2(buf + 4, idx);
 
1499
    dst = buf + 6;
 
1500
 
 
1501
    /* write the buckets */
 
1502
    for (i = ext->bucket_cnt, bp = ext->buckets ; i != 0 ; --i, ++bp)
 
1503
    {
 
1504
        /* write this bucket's index */
 
1505
        idx = ext->val_to_img_idx(*bp);
 
1506
        oswp2(dst, idx);
 
1507
        dst += 2;
 
1508
    }
 
1509
 
 
1510
    /* write the values */
 
1511
    for (i = ext->value_cnt, val = ext->idx_to_val(0) ; i != 0 ; --i, ++val)
 
1512
    {
 
1513
        /* store the key, value, and index */
 
1514
        vmb_put_dh(dst, &val->key);
 
1515
        vmb_put_dh(dst + VMB_DATAHOLDER, &val->val);
 
1516
        idx = ext->val_to_img_idx(val->nxt);
 
1517
        oswp2(dst + VMB_DATAHOLDER*2, idx);
 
1518
 
 
1519
        /* skip the data in the output */
 
1520
        dst += VMLOOKUP_VALUE_SIZE;
 
1521
    }
 
1522
 
 
1523
    /* return the size */
 
1524
    return copy_size;
 
1525
}
 
1526
 
 
1527
/*
 
1528
 *   Convert to constant data.  We must convert each object we reference in
 
1529
 *   a key or in a value to constant data if possible.
 
1530
 */
 
1531
void CVmObjLookupTable::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
1532
                                              vm_obj_id_t self)
 
1533
{
 
1534
    uint i;
 
1535
    vm_lookup_val *entry;
 
1536
 
 
1537
    /* run through each entry */
 
1538
    for (i = get_entry_count(), entry = get_ext()->idx_to_val(0) ;
 
1539
         i != 0 ; --i, ++entry)
 
1540
    {
 
1541
        /* convert the key */
 
1542
        convert_val_to_const_data(vmg_ mapper, &entry->key);
 
1543
 
 
1544
        /* convert the value */
 
1545
        convert_val_to_const_data(vmg_ mapper, &entry->val);
 
1546
    }
 
1547
}
 
1548
 
 
1549
/* ------------------------------------------------------------------------ */
 
1550
/*
 
1551
 *   IntrinsicClass Metaclass implementation - image rebuilding 
 
1552
 */
 
1553
 
 
1554
/*
 
1555
 *   Build an image file record 
 
1556
 */
 
1557
ulong CVmObjClass::rebuild_image(VMG_ char *buf, ulong buflen)
 
1558
{
 
1559
    size_t copy_size;
 
1560
 
 
1561
    /* get our size */
 
1562
    copy_size = osrp2(ext_);
 
1563
 
 
1564
    /* make sure we have room for our data */
 
1565
    if (copy_size > buflen)
 
1566
        return copy_size;
 
1567
 
 
1568
    /* copy the data */
 
1569
    memcpy(buf, ext_, copy_size);
 
1570
 
 
1571
    /* return the size */
 
1572
    return copy_size;
 
1573
}
 
1574
 
 
1575
/* ------------------------------------------------------------------------ */
 
1576
/*
 
1577
 *   Indexed Iterator
 
1578
 */
 
1579
 
 
1580
/*
 
1581
 *   Build an image file record 
 
1582
 */
 
1583
ulong CVmObjIterIdx::rebuild_image(VMG_ char *buf, ulong buflen)
 
1584
{
 
1585
    size_t copy_size;
 
1586
 
 
1587
    /* calculate our data size - just store our entire extension */
 
1588
    copy_size = VMOBJITERIDX_EXT_SIZE;
 
1589
 
 
1590
    /* make sure we have room for our data */
 
1591
    if (copy_size > buflen)
 
1592
        return copy_size;
 
1593
 
 
1594
    /* copy the data */
 
1595
    memcpy(buf, ext_, copy_size);
 
1596
 
 
1597
    /* return the size */
 
1598
    return copy_size;
 
1599
}
 
1600
 
 
1601
/*
 
1602
 *   Convert to constant data.  We must convert our collection object
 
1603
 *   reference to constant data if possible.  
 
1604
 */
 
1605
void CVmObjIterIdx::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
1606
                                          vm_obj_id_t self)
 
1607
{
 
1608
    /* convert our collection reference to constant data if possible */
 
1609
    convert_dh_to_const_data(vmg_ mapper, ext_);
 
1610
}
 
1611
 
 
1612
 
 
1613
/* ------------------------------------------------------------------------ */
 
1614
/*
 
1615
 *   Hashtable Iterator 
 
1616
 */
 
1617
 
 
1618
/*
 
1619
 *   Build an image file record 
 
1620
 */
 
1621
ulong CVmObjIterLookupTable::rebuild_image(VMG_ char *buf, ulong buflen)
 
1622
{
 
1623
    size_t copy_size;
 
1624
 
 
1625
    /* calculate our data size - just store our entire extension */
 
1626
    copy_size = VMOBJITERLOOKUPTABLE_EXT_SIZE;
 
1627
 
 
1628
    /* make sure we have room for our data */
 
1629
    if (copy_size > buflen)
 
1630
        return copy_size;
 
1631
 
 
1632
    /* copy the data */
 
1633
    memcpy(buf, ext_, copy_size);
 
1634
 
 
1635
    /* return the size */
 
1636
    return copy_size;
 
1637
}
 
1638
 
 
1639
/*
 
1640
 *   Convert to constant data.  We must convert our collection object
 
1641
 *   reference to constant data if possible.  
 
1642
 */
 
1643
void CVmObjIterLookupTable::convert_to_const_data(
 
1644
    VMG_ CVmConstMapper *mapper, vm_obj_id_t self)
 
1645
{
 
1646
    /* convert our collection reference to constant data if possible */
 
1647
    convert_dh_to_const_data(vmg_ mapper, ext_);
 
1648
}
 
1649
 
 
1650
 
 
1651
/* ------------------------------------------------------------------------ */
 
1652
/*
 
1653
 *   Vector Metaclass implementation - image rebuilding 
 
1654
 */
 
1655
 
 
1656
/*
 
1657
 *   Build an image file record 
 
1658
 */
 
1659
ulong CVmObjVector::rebuild_image(VMG_ char *buf, ulong buflen)
 
1660
{
 
1661
    size_t copy_size;
 
1662
    size_t ele_cnt;
 
1663
    size_t alloc_cnt;
 
1664
 
 
1665
    /* 
 
1666
     *   calculate how much space we need to store the data - store only
 
1667
     *   the data, not the undo bits 
 
1668
     */
 
1669
    ele_cnt = get_element_count();
 
1670
    alloc_cnt = get_allocated_count();
 
1671
    copy_size = 2*VMB_LEN + calc_alloc_ele(ele_cnt);
 
1672
 
 
1673
    /* make sure we have room for our data */
 
1674
    if (copy_size > buflen)
 
1675
        return copy_size;
 
1676
 
 
1677
    /* save the allocation count and in-use element count */
 
1678
    vmb_put_len(buf, alloc_cnt);
 
1679
    vmb_put_len(buf + VMB_LEN, ele_cnt);
 
1680
 
 
1681
    /* copy the element data */
 
1682
    memcpy(buf + 2*VMB_LEN, get_element_ptr(0), calc_alloc_ele(ele_cnt));
 
1683
 
 
1684
    /* return the size */
 
1685
    return copy_size;
 
1686
}
 
1687
 
 
1688
/*
 
1689
 *   Reserve space in a rebuilt constant pool for converting our data to a
 
1690
 *   constant value.
 
1691
 */
 
1692
void CVmObjVector::reserve_const_data(VMG_ CVmConstMapper *mapper,
 
1693
                                      vm_obj_id_t self)
 
1694
{
 
1695
    /* Vector values cannot be converted to constants */
 
1696
}
 
1697
 
 
1698
 
 
1699
/*
 
1700
 *   Convert to constant data.  We must convert each object we reference to
 
1701
 *   constant data if possible; we use the same algorithm as CVmObjList.  
 
1702
 */
 
1703
void CVmObjVector::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
1704
                                        vm_obj_id_t self)
 
1705
{
 
1706
    size_t cnt;
 
1707
    char *p;
 
1708
 
 
1709
    /* get my element count */
 
1710
    cnt = get_element_count();
 
1711
 
 
1712
    /* mark as referenced each object in our list */
 
1713
    for (p = get_element_ptr(0) ; cnt != 0 ; --cnt, inc_element_ptr(&p))
 
1714
    {
 
1715
        /* convert the value to constant data if possible */
 
1716
        convert_dh_to_const_data(vmg_ mapper, p);
 
1717
    }
 
1718
}
 
1719
 
 
1720
/* ------------------------------------------------------------------------ */
 
1721
/*
 
1722
 *   ByteArray Metaclass implementation - image rebuilding 
 
1723
 */
 
1724
 
 
1725
/*
 
1726
 *   Build an image file record 
 
1727
 */
 
1728
ulong CVmObjByteArray::rebuild_image(VMG_ char *buf, ulong buflen)
 
1729
{
 
1730
    ulong copy_size;
 
1731
    ulong rem;
 
1732
    ulong idx;
 
1733
 
 
1734
    /* we need four bytes for our count plus space for our byte array */
 
1735
    copy_size = 4 + get_element_count();
 
1736
 
 
1737
    /* make sure we have room for our data */
 
1738
    if (copy_size > buflen)
 
1739
        return copy_size;
 
1740
 
 
1741
    /* store our element count */
 
1742
    oswp4(buf, get_element_count());
 
1743
    buf += 4;
 
1744
 
 
1745
    /* copy our data in chunks */
 
1746
    for (idx = 1, rem = get_element_count() ; rem != 0 ; )
 
1747
    {
 
1748
        unsigned char *p;
 
1749
        size_t avail;
 
1750
        size_t chunk;
 
1751
 
 
1752
        /* get the next chunk */
 
1753
        p = get_ele_ptr(idx, &avail);
 
1754
 
 
1755
        /* limit copying to the remaining size */
 
1756
        chunk = avail;
 
1757
        if (chunk > rem)
 
1758
            chunk = rem;
 
1759
 
 
1760
        /* store this chunk */
 
1761
        memcpy(buf, p, chunk);
 
1762
 
 
1763
        /* skip this chunk */
 
1764
        buf += chunk;
 
1765
        idx += chunk;
 
1766
        rem -= chunk;
 
1767
    }
 
1768
 
 
1769
    /* return the size */
 
1770
    return copy_size;
 
1771
}
 
1772
 
 
1773
/* ------------------------------------------------------------------------ */
 
1774
/*
 
1775
 *   CharacterSet Metaclass implementation - image rebuilding 
 
1776
 */
 
1777
 
 
1778
/*
 
1779
 *   Build an image file record 
 
1780
 */
 
1781
ulong CVmObjCharSet::rebuild_image(VMG_ char *buf, ulong buflen)
 
1782
{
 
1783
    ulong copy_size;
 
1784
 
 
1785
    /* we need two bytes for the name length count plus the name's bytes */
 
1786
    copy_size = 2 + get_ext_ptr()->charset_name_len;
 
1787
 
 
1788
    /* make sure we have room for our data */
 
1789
    if (copy_size > buflen)
 
1790
        return copy_size;
 
1791
 
 
1792
    /* store the length of the name, and the name itself */
 
1793
    oswp2(buf, get_ext_ptr()->charset_name_len);
 
1794
    memcpy(buf + 2, get_ext_ptr()->charset_name,
 
1795
           get_ext_ptr()->charset_name_len);
 
1796
 
 
1797
    /* return the size */
 
1798
    return copy_size;
 
1799
}
 
1800
 
 
1801
/* ------------------------------------------------------------------------ */
 
1802
/*
 
1803
 *   File Metaclass implementation - image rebuilding 
 
1804
 */
 
1805
 
 
1806
/*
 
1807
 *   Build an image file record 
 
1808
 */
 
1809
ulong CVmObjFile::rebuild_image(VMG_ char *buf, ulong buflen)
 
1810
{
 
1811
    ulong copy_size;
 
1812
 
 
1813
    /* 
 
1814
     *   we need the character set object ID, the mode byte, the access
 
1815
     *   byte, and the uint32 flags 
 
1816
     */
 
1817
    copy_size = VMB_OBJECT_ID + 1 + 1 + 4;
 
1818
 
 
1819
    /* make sure we have room for our data */
 
1820
    if (copy_size > buflen)
 
1821
        return copy_size;
 
1822
 
 
1823
    /* store the character set object ID */
 
1824
    vmb_put_objid(buf, get_ext()->charset);
 
1825
    buf += VMB_OBJECT_ID;
 
1826
 
 
1827
    /* store the mode and access values */
 
1828
    *buf++ = get_ext()->mode;
 
1829
    *buf++ = get_ext()->access;
 
1830
 
 
1831
    /* store the flags */
 
1832
    oswp4(buf, get_ext()->flags);
 
1833
 
 
1834
    /* return the size */
 
1835
    return copy_size;
 
1836
}
 
1837
 
 
1838
/* ------------------------------------------------------------------------ */
 
1839
/*
 
1840
 *   Pattern metaclass 
 
1841
 */
 
1842
 
 
1843
/*
 
1844
 *   build an image file record for the object 
 
1845
 */
 
1846
ulong CVmObjPattern::rebuild_image(VMG_ char *buf, ulong buflen)
 
1847
{
 
1848
    size_t need_size;
 
1849
 
 
1850
    /* we need a DATAHOLDER to store the original pattern string reference */
 
1851
    need_size = VMB_DATAHOLDER;
 
1852
 
 
1853
    /* if we need more space, just return our size requirements */
 
1854
    if (need_size > buflen)
 
1855
        return need_size;
 
1856
 
 
1857
    /* write our value */
 
1858
    vmb_put_dh(buf, get_orig_str());
 
1859
 
 
1860
    /* return our size */
 
1861
    return need_size;
 
1862
}
 
1863
 
 
1864
/*
 
1865
 *   Convert to constant data.
 
1866
 */
 
1867
void CVmObjPattern::convert_to_const_data(VMG_ CVmConstMapper *mapper,
 
1868
                                          vm_obj_id_t /*self*/)
 
1869
{
 
1870
    /* check our original source string value */
 
1871
    convert_val_to_const_data(vmg_ mapper, &get_ext()->str);
 
1872
}
 
1873
 
 
1874
/* ------------------------------------------------------------------------ */
 
1875
/*
 
1876
 *   StringComparator intrinsic class 
 
1877
 */
 
1878
 
 
1879
/*
 
1880
 *   build the image data 
 
1881
 */
 
1882
ulong CVmObjStrComp::rebuild_image(VMG_ char *buf, ulong buflen)
 
1883
{
 
1884
    /* set up a stream writer on the buffer, and write it out */
 
1885
    CVmMemoryStream str(buf, buflen);
 
1886
    return write_to_stream(vmg_ &str, &buflen);
 
1887
}
 
1888