cfad47cfa3/tads3/vmbytarr.cpp

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* 
2
 *   Copyright (c) 2001, 2002 Michael J. Roberts.  All Rights Reserved.
3
 *   
4
 *   Please see the accompanying license file, LICENSE.TXT, for information
5
 *   on using and copying this software.  
6
 */
7
/*
8
Name
9
  vmbytarr.cpp - TADS 3 ByteArray intrinsic class
10
Function
11
  
12
Notes
13
  
14
Modified
15
  06/05/01 MJRoberts  - Creation
16
*/
17
18
#include <stdlib.h>
19
#include <assert.h>
20
#include "vmtype.h"
21
#include "vmobj.h"
22
#include "vmglob.h"
23
#include "vmbytarr.h"
24
#include "vmbif.h"
25
#include "vmfile.h"
26
#include "vmerrnum.h"
27
#include "vmerr.h"
28
#include "vmstack.h"
29
#include "vmmeta.h"
30
#include "vmundo.h"
31
#include "vmrun.h"
32
#include "charmap.h"
33
#include "vmstr.h"
34
#include "vmcset.h"
35
36
/* ------------------------------------------------------------------------ */
37
/*
38
 *   Integer format codes.  A full format code is given by a bitwise
39
 *   combination of size, byte order, and signedness.  
40
 */
41
42
/* integer sizes */
43
#define FmtSizeMask           0x000F
44
#define FmtInt8               0x0000
45
#define FmtInt16              0x0001
46
#define FmtInt32              0x0002
47
48
/* integer byte orders */
49
#define FmtOrderMask          0x00F0
50
#define FmtLittleEndian       0x0000
51
#define FmtBigEndian          0x0010
52
53
/* integer signedness */
54
#define FmtSignedMask         0x0F00
55
#define FmtSigned             0x0000
56
#define FmtUnsigned           0x0100
57
58
/* ------------------------------------------------------------------------ */
59
/*
60
 *   statics 
61
 */
62
63
/* metaclass registration object */
64
static CVmMetaclassByteArray metaclass_reg_obj;
65
CVmMetaclass *CVmObjByteArray::metaclass_reg_ = &metaclass_reg_obj;
66
67
/* function table */
68
int (CVmObjByteArray::
69
     *CVmObjByteArray::func_table_[])(VMG_ vm_obj_id_t self,
70
                                      vm_val_t *retval, uint *argc) =
71
{
72
    &CVmObjByteArray::getp_undef,
73
    &CVmObjByteArray::getp_length,
74
    &CVmObjByteArray::getp_subarray,
75
    &CVmObjByteArray::getp_copy_from,
76
    &CVmObjByteArray::getp_fill_val,
77
    &CVmObjByteArray::getp_to_string,
78
    &CVmObjByteArray::getp_read_int,
79
    &CVmObjByteArray::getp_write_int
80
};
81
82
83
/* ------------------------------------------------------------------------ */
84
/*
85
 *   Create from stack 
86
 */
87
vm_obj_id_t CVmObjByteArray::create_from_stack(VMG_ const uchar **pc_ptr,
88
                                               uint argc)
89
{
90
    vm_obj_id_t id = VM_INVALID_OBJ;
91
    CVmObjByteArray *arr;
92
    unsigned long cnt;
93
    vm_val_t *arg1;
94
95
    /* check our arguments */
96
    if (argc < 1)
97
        err_throw(VMERR_WRONG_NUM_OF_ARGS);
98
99
    /* see what we have for the first argument */
100
    arg1 = G_stk->get(0);
101
    if (arg1->typ == VM_INT)
102
    {
103
        /* 
104
         *   it's a simple count argument - make sure we only have one
105
         *   argument 
106
         */
107
        if (argc != 1)
108
            err_throw(VMERR_WRONG_NUM_OF_ARGS);
109
110
        /* get the number of elements */
111
        cnt = (unsigned long)arg1->val.intval;
112
113
        /* create the array with the given number of elements */
114
        id = vm_new_id(vmg_ FALSE, FALSE, FALSE);
115
        arr = new (vmg_ id) CVmObjByteArray(vmg_ cnt);
116
117
        /* set each element to zero */
118
        arr->fill_with(0, 1, cnt);
119
    }
120
    else if (arg1->typ == VM_OBJ && is_byte_array(vmg_ arg1->val.obj))
121
    {
122
        unsigned long src_idx;
123
        unsigned long src_cnt;
124
        CVmObjByteArray *src_arr;
125
126
        /* remember the source array */
127
        src_arr = (CVmObjByteArray *)vm_objp(vmg_ arg1->val.obj);
128
129
        /* get the count from the array */
130
        src_cnt = src_arr->get_element_count();
131
132
        /* 
133
         *   check for the optional actual element count, and the optional
134
         *   starting index 
135
         */
136
        if (argc == 1)
137
        {
138
            /* no size specified - use the same size as the original */
139
            cnt = src_cnt;
140
141
            /* start at the first element */
142
            src_idx = 1;
143
        }
144
        else
145
        {
146
            /* make sure it's an integer */
147
            if (G_stk->get(1)->typ != VM_INT)
148
                err_throw(VMERR_INT_VAL_REQD);
149
150
            /* use the specified starting index */
151
            src_idx = (unsigned long)G_stk->get(1)->val.intval;
152
153
            /* if the index is below 1, force it to 1 */
154
            if (src_idx < 1)
155
                src_idx = 1;
156
157
            /* check for the starting element argument */
158
            if (argc >= 3)
159
            {
160
                /* make sure it's an integer */
161
                if (G_stk->get(2)->typ != VM_INT)
162
                    err_throw(VMERR_INT_VAL_REQD);
163
164
                /* use the specified element count */
165
                cnt = (unsigned long)G_stk->get(2)->val.intval;
166
167
                /* make sure we don't have any extra arguments */
168
                if (argc > 3)
169
                    err_throw(VMERR_WRONG_NUM_OF_ARGS);
170
            }
171
            else
172
            {
173
                /* 
174
                 *   no count specified - use the number of elements in the
175
                 *   original remaining after the starting index 
176
                 */
177
                if (src_idx > src_cnt)
178
                    cnt = 0;
179
                else
180
                    cnt = src_cnt + 1 - src_idx;
181
            }
182
        }
183
184
        /* create the new array */
185
        id = vm_new_id(vmg_ FALSE, FALSE, FALSE);
186
        arr = new (vmg_ id) CVmObjByteArray(vmg_ cnt);
187
188
        /* copy the source array */
189
        arr->copy_from(1, src_arr, src_idx, cnt);
190
    }
191
    else
192
    {
193
        /* invalid argument */
194
        err_throw(VMERR_BAD_TYPE_BIF);
195
    }
196
197
    /* discard arguments */
198
    G_stk->discard(argc);
199
200
    /* return the new object */
201
    return id;
202
}
203
204
/* ------------------------------------------------------------------------ */
205
/*
206
 *   Create with no contents 
207
 */
208
vm_obj_id_t CVmObjByteArray::create(VMG_ int in_root_set)
209
{
210
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE);
211
    new (vmg_ id) CVmObjByteArray();
212
    return id;
213
}
214
215
/*
216
 *   Create with the given element count 
217
 */
218
vm_obj_id_t CVmObjByteArray::create(VMG_ int in_root_set,
219
                                    unsigned long ele_count)
220
{
221
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, FALSE, FALSE);
222
    new (vmg_ id) CVmObjByteArray(vmg_ ele_count);
223
    return id;
224
}
225
226
/* ------------------------------------------------------------------------ */
227
/*
228
 *   Instantiate 
229
 */
230
CVmObjByteArray::CVmObjByteArray(VMG_ unsigned long ele_count)
231
{
232
    /* allocate space */
233
    alloc_array(vmg_ ele_count);
234
}
235
236
/* ------------------------------------------------------------------------ */
237
/*
238
 *   Allocate space 
239
 */
240
void CVmObjByteArray::alloc_array(VMG_ unsigned long ele_count)
241
{
242
    unsigned long ele_rem;
243
    size_t slot_cnt;
244
    size_t alloc_size;
245
    size_t i;
246
    
247
    /* 
248
     *   figure out how many first-level page tables we need - each
249
     *   first-level page table refers to 256M bytes (8k pages per
250
     *   second-level page table times 32k bytes per page), so we need one
251
     *   page table pointer per 256 megabytes == 2^15*2^32 == 2^28.  
252
     */
253
    slot_cnt = (ele_count == 0 ? 0 : ((size_t)((ele_count - 1) >> 28) + 1));
254
255
    /* 
256
     *   allocate the extension - 4 bytes for the element count plus one
257
     *   (char **) per slot 
258
     */
259
    alloc_size = 4 + slot_cnt*sizeof(char **);
260
    ext_ = (char *)G_mem->get_var_heap()->alloc_mem(alloc_size, this);
261
262
    /* set the element count */
263
    set_element_count(ele_count);
264
265
    /* allocate the second-level page tables */
266
    for (ele_rem = ele_count, i = 0 ; i < slot_cnt ; ++i)
267
    {
268
        unsigned char **pgtab;
269
        unsigned long pgcnt;
270
        size_t pg;
271
        
272
        /* 
273
         *   Determine how many pages we need in this page table.  Each page
274
         *   holds 32k bytes, so we need one page per 32k bytes remaining to
275
         *   allocate.  However, each page table can hold only up to 8k page
276
         *   pointers, so limit this table to 8k pages. 
277
         */
278
        pgcnt = ((ele_rem - 1) >> 15) + 1;
279
        if (pgcnt > 8*1024)
280
            pgcnt = 8*1024;
281
282
        /* allocate this second-level page table */
283
        pgtab = (unsigned char **)t3malloc(
284
            (size_t)(pgcnt * sizeof(unsigned char *)));
285
286
        /* set this page table pointer */
287
        get_page_table_array()[i] = pgtab;
288
289
        /* allocate the pages in this table */
290
        for (pg = 0 ; pg < pgcnt ; ++pg)
291
        {
292
            size_t pgsiz;
293
294
            /* 
295
             *   calculate the size for this page - the maximum size of a
296
             *   page is 32k, but allocate only the amount remaining if we
297
             *   have less than 32k left to allocate 
298
             */
299
            pgsiz = 32*1024;
300
            if (pgsiz > ele_rem)
301
                pgsiz = (size_t)ele_rem;
302
303
            /* deduct this page's size from the remaining element count */
304
            ele_rem -= pgsiz;
305
306
            /* allocate and store this page */
307
            pgtab[pg] = (unsigned char *)t3malloc(pgsiz);
308
        }
309
    }
310
}
311
312
/* ------------------------------------------------------------------------ */
313
/*
314
 *   Notify of deletion
315
 */
316
void CVmObjByteArray::notify_delete(VMG_ int /*in_root_set*/)
317
{
318
    size_t slot;
319
    unsigned char ***slotp;
320
    size_t slot_cnt;
321
    size_t bytes_rem;
322
323
    /* if we have no extension, there's nothing to do */
324
    if (ext_ == 0)
325
        return;
326
327
    /* calculate the number of second-level page table slots */
328
    slot_cnt = (size_t)(get_element_count() == 0
329
                        ? 0
330
                        : ((get_element_count() >> 28) + 1));
331
332
    /* we have all of the bytes in the array left do delete */
333
    bytes_rem = get_element_count();
334
335
    /* traverse the list of second-level page tables and delete each one */
336
    for (slot = 0, slotp = get_page_table_array() ; slot < slot_cnt ;
337
         ++slot, ++slotp)
338
    {
339
        size_t pg;
340
        unsigned char **pgp;
341
        
342
        /* traverse each page in this page table */
343
        for (pg = 0, pgp = *slotp ; pg < 8*1024 && bytes_rem != 0 ;
344
             ++pg, ++pgp)
345
        {
346
            /* delete this page */
347
            t3free(*pgp);
348
349
            /* 
350
             *   deduct this page's size from the remaining bytes to delete
351
             *   - the page size is up to 32k, but no more than the
352
             *   remaining size
353
             */
354
            if (bytes_rem > 32*1024)
355
                bytes_rem -= 32*1024;
356
            else
357
                bytes_rem = 0;
358
        }
359
360
        /* delete this page table */
361
        t3free(*slotp);
362
    }
363
364
    /* free our extension */
365
    G_mem->get_var_heap()->free_mem(ext_);
366
}
367
368
/* ------------------------------------------------------------------------ */
369
/* 
370
 *   set a property 
371
 */
372
void CVmObjByteArray::set_prop(VMG_ class CVmUndo *,
373
                               vm_obj_id_t, vm_prop_id_t,
374
                               const vm_val_t *)
375
{
376
    err_throw(VMERR_INVALID_SETPROP);
377
}
378
379
/* ------------------------------------------------------------------------ */
380
/* 
381
 *   get a property 
382
 */
383
int CVmObjByteArray::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
384
                              vm_obj_id_t self, vm_obj_id_t *source_obj,
385
                              uint *argc)
386
{
387
    uint func_idx;
388
389
    /* translate the property index to an index into our function table */
390
    func_idx = G_meta_table
391
               ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
392
    
393
    /* call the appropriate function */
394
    if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
395
    {
396
        *source_obj = metaclass_reg_->get_class_obj(vmg0_);
397
        return TRUE;
398
    }
399
    
400
    /* inherit default handling */
401
    return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc);
402
}
403
404
/* ------------------------------------------------------------------------ */
405
/*
406
 *   ByteArray private undo record - saved byte block 
407
 */
408
struct bytearray_undo_bytes
409
{
410
    /* next block in the list */
411
    bytearray_undo_bytes *nxt_;
412
413
    /* the bytes - overallocated to the required size */
414
    unsigned char buf_[1];
415
};
416
417
/*
418
 *   ByteArray private undo record 
419
 */
420
class bytearray_undo_rec
421
{
422
public:
423
    bytearray_undo_rec(CVmObjByteArray *arr, unsigned long start_idx,
424
                       unsigned long cnt)
425
    {
426
        unsigned long idx;
427
        unsigned long rem;
428
        bytearray_undo_bytes *tail;
429
430
        /* remember the starting index and length of the saved data */
431
        idx_ = start_idx;
432
        cnt_ = cnt;
433
        
434
        /* store the original data in a linked list of byte blocks */
435
        for (idx = start_idx, rem = cnt, save_head_ = tail = 0 ;
436
             rem != 0 ; )
437
        {
438
            bytearray_undo_bytes *p;
439
            size_t cur;
440
441
            /* allocate up to 32k, or the amount remaining if lower */
442
            cur = 32768;
443
            if (cur > rem)
444
                cur = (size_t)rem;
445
            
446
            /* allocate a new block */
447
            p = (bytearray_undo_bytes *)t3malloc(
448
                sizeof(bytearray_undo_bytes) + cur - 1);
449
450
            /* link this block into our list */
451
            if (tail != 0)
452
                tail->nxt_ = p;
453
            else
454
                save_head_ = p;
455
            tail = p;
456
            p->nxt_ = 0;
457
                
458
            /* copy bytes into this block */
459
            arr->copy_to_buf(p->buf_, idx, cur);
460
461
            /* move past the copied bytes */
462
            idx += cur;
463
            rem -= cur;
464
        }
465
    }
466
467
    ~bytearray_undo_rec()
468
    {
469
        bytearray_undo_bytes *cur;
470
        bytearray_undo_bytes *nxt;
471
472
        /* delete our list of saved byte blocks */
473
        for (cur = save_head_ ; cur != 0 ; cur = nxt)
474
        {
475
            /* 
476
             *   remember the next one, as we're about to lose the current
477
             *   one's memory 
478
             */
479
            nxt = cur->nxt_;
480
481
            /* delete this one */
482
            t3free(cur);
483
        }
484
    }
485
486
    /* copy our data back into an array */
487
    void apply_undo(CVmObjByteArray *arr)
488
    {
489
        bytearray_undo_bytes *cur;
490
        unsigned long idx;
491
        unsigned long rem;
492
493
        /* delete our list of saved byte blocks */
494
        for (cur = save_head_, idx = idx_, rem = cnt_ ; rem != 0 ; )
495
        {
496
            size_t copy_len;
497
            
498
            /* limit the copy to our block size (32k) */
499
            copy_len = 32768;
500
            if (copy_len > rem)
501
                copy_len = (size_t)rem;
502
503
            /* copy this block into the array */
504
            arr->copy_from_buf(cur->buf_, idx, copy_len);
505
506
            /* move past the copied data */
507
            cur = cur->nxt_;
508
            idx += copy_len;
509
            rem -= copy_len;
510
        }
511
    }
512
513
    /* starting index */
514
    unsigned long idx_;
515
516
    /* length */
517
    unsigned long cnt_;
518
519
    /* head of list of blocks of saved data */
520
    bytearray_undo_bytes *save_head_;
521
};
522
523
/*
524
 *   Save undo for a change to a range of the array 
525
 */
526
void CVmObjByteArray::save_undo(VMG_ vm_obj_id_t self,
527
                                unsigned long start_idx,
528
                                unsigned long cnt)
529
{
530
    bytearray_undo_rec *rec;
531
    vm_val_t oldval;
532
    
533
    /* create our key record - this contains the entire original value */
534
    rec = new bytearray_undo_rec(this, start_idx, cnt);
535
536
    /* we don't use the old value for anything; use nil as a dummy */
537
    oldval.set_nil();
538
539
    /* add the undo record */
540
    if (!G_undo->add_new_record_ptr_key(vmg_ self, rec, &oldval))
541
    {
542
        /* failed to save the undo - discard our private key record */
543
        delete rec;
544
    }
545
}
546
547
/*
548
 *   Apply undo 
549
 */
550
void CVmObjByteArray::apply_undo(VMG_ struct CVmUndoRecord *gen_rec)
551
{
552
    /* apply our private undo record, if present */
553
    if (gen_rec->id.ptrval != 0)
554
    {
555
        bytearray_undo_rec *rec;
556
557
        /* get my private record */
558
        rec = (bytearray_undo_rec *)gen_rec->id.ptrval;
559
        
560
        /* apply the undo in the record to self */
561
        rec->apply_undo(this);
562
563
        /* delete the record now that it's been applied */
564
        delete rec;
565
566
        /* clear the pointer so we know it's gone */
567
        gen_rec->id.ptrval = 0;
568
    }
569
}
570
571
/*
572
 *   Discard undo 
573
 */
574
void CVmObjByteArray::discard_undo(VMG_ struct CVmUndoRecord *rec)
575
{
576
    /* delete our extra information record if present */
577
    if (rec->id.ptrval != 0)
578
    {
579
        /* free the record */
580
        t3free((bytearray_undo_rec *)rec->id.ptrval);
581
582
        /* clear the pointer so we know it's gone */
583
        rec->id.ptrval = 0;
584
    }
585
}
586
587
/* ------------------------------------------------------------------------ */
588
/*
589
 *   load from an image file 
590
 */
591
void CVmObjByteArray::load_from_image(VMG_ vm_obj_id_t self,
592
                                      const char *ptr, size_t siz)
593
{
594
    /* load from the image data */
595
    load_image_data(vmg_ ptr, siz);
596
597
    /* 
598
     *   save our image data pointer in the object table, so that we can
599
     *   access it (without storing it ourselves) during a reload 
600
     */
601
    G_obj_table->save_image_pointer(self, ptr, siz);
602
}
603
604
/*
605
 *   reload the object from image data 
606
 */
607
void CVmObjByteArray::reload_from_image(VMG_ vm_obj_id_t,
608
                                        const char *ptr, size_t siz)
609
{
610
    /* load the image data */
611
    load_image_data(vmg_ ptr, siz);
612
}
613
614
/*
615
 *   Load from image data 
616
 */
617
void CVmObjByteArray::load_image_data(VMG_ const char *ptr, size_t siz)
618
{
619
    unsigned long cnt;
620
    unsigned long idx;
621
622
    /* if we already have memory allocated, free it */
623
    notify_delete(vmg_ FALSE);
624
625
    /* get the new array size */
626
    cnt = t3rp4u(ptr);
627
628
    /* make sure the size isn't larger than we'd expect */
629
    if (siz > 4 + cnt)
630
        siz = 4 + cnt;
631
632
    /* allocate memory at the new size as indicated in the image data */
633
    alloc_array(vmg_ cnt);
634
635
    /* if the size is smaller than we'd expect, set extra elements to nil */
636
    if (siz < VMB_LEN + (VMB_DATAHOLDER * cnt))
637
    {
638
        /* fill everything with zeroes to start with */
639
        fill_with(0, 1, cnt);
640
    }
641
642
    /* copy the bytes */
643
    for (ptr += 4, siz -= 4, idx = 1 ; siz != 0 ; )
644
    {
645
        unsigned char *dstp;
646
        size_t chunk_size;
647
        size_t avail;
648
649
        /* get the next chunk */
650
        dstp = get_ele_ptr(idx, &avail);
651
652
        /* limit this chunk size to the remaining copy size */
653
        chunk_size = avail;
654
        if (chunk_size > siz)
655
            chunk_size = siz;
656
657
        /* copy this chunk */
658
        memcpy(dstp, ptr, chunk_size);
659
660
        /* advance past this chunk */
661
        idx += chunk_size;
662
        ptr += chunk_size;
663
        siz -= chunk_size;
664
    }
665
}
666
667
/* ------------------------------------------------------------------------ */
668
/* 
669
 *   save to a file 
670
 */
671
void CVmObjByteArray::save_to_file(VMG_ class CVmFile *fp)
672
{
673
    unsigned long rem;
674
    unsigned long idx;
675
    
676
    /* write the element count */
677
    fp->write_int4(get_element_count());
678
679
    /* write the bytes in chunks */
680
    for (idx = 1, rem = get_element_count() ; rem != 0 ; )
681
    {
682
        size_t avail;
683
        size_t chunk;
684
        unsigned char *p;
685
686
        /* get the next chunk */
687
        p = get_ele_ptr(idx, &avail);
688
689
        /* limit this copy to the remaining bytes */
690
        chunk = avail;
691
        if (chunk > rem)
692
            chunk = (size_t)rem;
693
694
        /* write this chunk */
695
        fp->write_bytes((char *)p, chunk);
696
697
        /* advance past this chunk */
698
        idx += chunk;
699
        rem -= chunk;
700
    }
701
}
702
703
/* 
704
 *   restore from a file 
705
 */
706
void CVmObjByteArray::restore_from_file(VMG_ vm_obj_id_t self,
707
                                        CVmFile *fp, CVmObjFixup *)
708
{
709
    unsigned long ele_cnt;
710
    unsigned long idx;
711
    unsigned long rem;
712
713
    /* read the element count */
714
    ele_cnt = fp->read_uint4();
715
716
    /* allocate or reallocate as needed */
717
    if (ext_ == 0)
718
    {
719
        /* we're not yet allocated - allocate now */
720
        alloc_array(vmg_ ele_cnt);
721
    }
722
    else
723
    {
724
        /* already allocated - if it's a different size, reallocate */
725
        if (get_element_count() != ele_cnt)
726
        {
727
            /* delete the old array */
728
            notify_delete(vmg_ FALSE);
729
730
            /* allocate a new one */
731
            alloc_array(vmg_ ele_cnt);
732
        }
733
    }
734
735
    /* read the data */
736
    for (idx = 1, rem = ele_cnt ; rem != 0 ; )
737
    {
738
        size_t avail;
739
        size_t chunk;
740
        unsigned char *p;
741
742
        /* get the next chunk */
743
        p = get_ele_ptr(idx, &avail);
744
745
        /* limit this copy to the remaining bytes */
746
        chunk = avail;
747
        if (chunk > rem)
748
            chunk = (size_t)rem;
749
750
        /* read this chunk */
751
        fp->read_bytes((char *)p, chunk);
752
753
        /* advance past this chunk */
754
        idx += chunk;
755
        rem -= chunk;
756
    }
757
}
758
759
/* ------------------------------------------------------------------------ */
760
/*
761
 *   Retrieve the value at the given index 
762
 */
763
void CVmObjByteArray::index_val(VMG_ vm_val_t *result, vm_obj_id_t self,
764
                                const vm_val_t *index_val)
765
{
766
    unsigned char *p;
767
    size_t avail;
768
    unsigned long idx;
769
770
    /* get the index */
771
    idx = index_val->num_to_int();
772
773
    /* make sure it's in range */
774
    if (idx < 1 || idx > get_element_count())
775
        err_throw(VMERR_INDEX_OUT_OF_RANGE);
776
    
777
    /* get a pointer to the desired element */
778
    p = get_ele_ptr(idx, &avail);
779
780
    /* return the value as an integer */
781
    result->set_int((int)(unsigned short)*p);
782
}
783
784
/* ------------------------------------------------------------------------ */
785
/* 
786
 *   set an indexed element of the array 
787
 */
788
void CVmObjByteArray::set_index_val(VMG_ vm_val_t *new_container,
789
                                    vm_obj_id_t self,
790
                                    const vm_val_t *index_val,
791
                                    const vm_val_t *new_val)
792
{
793
    unsigned char *p;
794
    size_t avail;
795
    unsigned long idx;
796
    unsigned long new_byte;
797
798
    /* get the index value as an integer */
799
    idx = index_val->num_to_int();
800
801
    /* make sure it's in range - 1 to our element count, inclusive */
802
    if (idx < 1 || idx > get_element_count())
803
        err_throw(VMERR_INDEX_OUT_OF_RANGE);
804
805
    /* save undo for the change */
806
    save_undo(vmg_ self, idx, 1);
807
808
    /* get the new value as an integer */
809
    new_byte = new_val->num_to_int();
810
811
    /* make sure it's in range */
812
    if (new_byte > 255)
813
        err_throw(VMERR_OUT_OF_RANGE);
814
815
    /* get a pointer to the desired element */
816
    p = get_ele_ptr(idx, &avail);
817
818
    /* set the value */
819
    *p = (unsigned char)new_byte;
820
821
    /* the result is the original array value */
822
    new_container->set_obj(self);
823
}
824
825
/* ------------------------------------------------------------------------ */
826
/*
827
 *   Compare for equality 
828
 */
829
int CVmObjByteArray::equals(VMG_ vm_obj_id_t self, const vm_val_t *val,
830
                            int /*depth*/) const
831
{
832
    CVmObjByteArray *other;
833
    unsigned long idx;
834
    unsigned long rem;
835
836
    /* if it's a self-reference, it's certainly equal */
837
    if (val->typ == VM_OBJ && val->val.obj == self)
838
        return TRUE;
839
840
    /* if it's not another byte array, it's not equal */
841
    if (val->typ != VM_OBJ || !is_byte_array(vmg_ val->val.obj))
842
        return FALSE;
843
844
    /* we know it's another byte array - cast it */
845
    other = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj);
846
847
    /* if it's not of the same length, it's not equal */
848
    if (other->get_element_count() != get_element_count())
849
        return FALSE;
850
851
    /* compare the arrays */
852
    for (idx = 1, rem = get_element_count() ; rem != 0 ; )
853
    {
854
        unsigned char *p1;
855
        unsigned char *p2;
856
        size_t avail1;
857
        size_t avail2;
858
        size_t chunk;
859
        
860
        /* get the next chunk of each array */
861
        p1 = get_ele_ptr(idx, &avail1);
862
        p2 = other->get_ele_ptr(idx, &avail2);
863
864
        /* if the chunk sizes aren't the same, there's some problem */
865
        assert(avail1 == avail2);
866
867
        /* limit the chunk size to the remaining size */
868
        chunk = avail1;
869
        if (chunk > rem)
870
            chunk = (size_t)rem;
871
872
        /* if the chunks differ, the arrays differ */
873
        if (memcmp(p1, p2, chunk) != 0)
874
            return FALSE;
875
876
        /* advance past the chunk */
877
        idx += avail1;
878
        rem -= avail1;
879
    }
880
881
    /* we found no differences */
882
    return TRUE;
883
}
884
885
/*
886
 *   Calculate a hash value 
887
 */
888
uint CVmObjByteArray::calc_hash(VMG_ vm_obj_id_t self, int /*depth*/) const
889
{
890
    unsigned long idx;
891
    unsigned long rem;
892
    uint hash;
893
894
    /* add up the bytes in the array */
895
    for (hash = 0, idx = 1, rem = get_element_count() ; rem != 0 ; )
896
    {
897
        unsigned char *p;
898
        size_t avail;
899
        size_t chunk;
900
901
        /* get the next chunk */
902
        p = get_ele_ptr(idx, &avail);
903
904
        /* limit the chunk size to the remaining size */
905
        chunk = avail;
906
        if (chunk > rem)
907
            chunk = (size_t)rem;
908
909
        /* advance our counters for this chunk */
910
        idx += chunk;
911
        rem -= chunk;
912
913
        /* add up the bytes in this part */
914
        for ( ; chunk != 0 ; --chunk, ++p)
915
            hash += *p;
916
    }
917
918
    /* return the result */
919
    return hash;
920
}
921
922
/* ------------------------------------------------------------------------ */
923
/*
924
 *   Copy bytes from an array
925
 */
926
void CVmObjByteArray::copy_from(unsigned long dst_idx,
927
                                CVmObjByteArray *src_arr,
928
                                unsigned long src_idx,
929
                                unsigned long cnt)
930
{
931
    /* if we're moving zero bytes, there's nothing to do */
932
    if (cnt == 0)
933
        return;
934
935
    /* make sure we don't overrun our array */
936
    if (dst_idx > get_element_count())
937
        cnt = 0;
938
    else if (dst_idx + cnt - 1 > get_element_count())
939
        cnt = get_element_count() + 1 - dst_idx;
940
941
    /*
942
     *   If the source and destination objects are the same, and the source
943
     *   and destination regions overlap, we must take care to move the
944
     *   bytes in such a way that we don't overwrite parts of the source in
945
     *   the course of moving the bytes. 
946
     */
947
    if (src_arr == this
948
        && src_idx + cnt - 1 >= dst_idx && src_idx <= dst_idx + cnt - 1)
949
    {
950
        /* the regions overlap - use the overlap-safe move routine */
951
        move_bytes(dst_idx, src_idx, cnt);
952
953
        /* done */
954
        return;
955
    }
956
        
957
    /* continue until we exhaust the count */
958
    while (cnt != 0)
959
    {
960
        size_t src_avail;
961
        size_t src_chunk_size;
962
        unsigned char *srcp;
963
        
964
        /* if we're past the end of the source array, stop copying */
965
        if (src_idx > src_arr->get_element_count())
966
            break;
967
968
        /* get the next source chunk */
969
        srcp = src_arr->get_ele_ptr(src_idx, &src_avail);
970
971
        /* limit this chunk size to the remaining copy size */
972
        src_chunk_size = src_avail;
973
        if (src_chunk_size > cnt)
974
            src_chunk_size = (size_t)cnt;
975
976
        /* limit this chunk to the remaining source array size */
977
        if (src_idx + src_chunk_size - 1 > src_arr->get_element_count())
978
            src_chunk_size = src_arr->get_element_count() + 1 - src_idx;
979
980
        /* copy this chunk into the destination */
981
        copy_from_buf(srcp, dst_idx, src_chunk_size);
982
983
        /* move past this chunk in the source */
984
        cnt -= src_chunk_size;
985
        src_idx += src_chunk_size;
986
987
        /* move past this chunk in the destination */
988
        dst_idx += src_chunk_size;
989
    }
990
991
    /* 
992
     *   if there's any copying size left, we ran out of source array bytes
993
     *   - fill the balance of the destination array with zeroes
994
     */
995
    if (cnt != 0)
996
        fill_with(0, dst_idx, cnt);
997
}
998
999
/*
1000
 *   Move bytes within our array.  This is safe even if the source and
1001
 *   destination regions overlap.  
1002
 */
1003
void CVmObjByteArray::move_bytes(unsigned long dst_idx,
1004
                                 unsigned long src_idx,
1005
                                 unsigned long cnt)
1006
{
1007
    size_t src_avail;
1008
    size_t dst_avail;
1009
    unsigned char *srcp;
1010
    unsigned char *dstp;
1011
1012
    /* 
1013
     *   If the destination is before the source, we're moving bytes down,
1014
     *   so we must start at the low end and work forwards through the
1015
     *   array.  If the destination is after the source, we're moving bytes
1016
     *   up, so we must start at the high end and work backwards through the
1017
     *   array.  
1018
     */
1019
    if (dst_idx < src_idx)
1020
    {
1021
1022
        /* 
1023
         *   Moving bytes down in the array - start at the low end and work
1024
         *   forwards through the array.  Get the starting pointers and
1025
         *   available lengths.  
1026
         */
1027
        srcp = get_ele_ptr(src_idx, &src_avail);
1028
        dstp = get_ele_ptr(dst_idx, &dst_avail);
1029
1030
        /* keep going until we've moved all of the bytes requested */
1031
        while (cnt != 0)
1032
        {
1033
            size_t move_len;
1034
1035
            /* 
1036
             *   figure the largest amount we can move - we can move the
1037
             *   smallest of the remaining requested move size, the source
1038
             *   chunk, and the destination chunk 
1039
             */
1040
            move_len = cnt;
1041
            if (move_len > src_avail)
1042
                move_len = src_avail;
1043
            if (move_len > dst_avail)
1044
                move_len = dst_avail;
1045
1046
            /* move the data */
1047
            memmove(dstp, srcp, move_len);
1048
1049
            /* advance all of the counters by the move size */
1050
            srcp += move_len;
1051
            dstp += move_len;
1052
            cnt -= move_len;
1053
            src_avail -= move_len;
1054
            dst_avail -= move_len;
1055
            src_idx += move_len;
1056
            dst_idx += move_len;
1057
1058
            /* stop if we're done */
1059
            if (cnt == 0)
1060
                break;
1061
1062
            /* if the source chunk is at an end, get the next one */
1063
            if (src_avail == 0)
1064
                srcp = get_ele_ptr(src_idx, &src_avail);
1065
1066
            /* if the destination chunk is at an end, get the next one */
1067
            if (dst_avail == 0)
1068
                dstp = get_ele_ptr(dst_idx, &dst_avail);
1069
        }
1070
    }
1071
    else
1072
    {
1073
        /* 
1074
         *   We're to move bytes up in the array - start at the high end and
1075
         *   work backwards through the array.  Advance each index to one
1076
         *   past the last byte of its range.  
1077
         */
1078
        src_idx += cnt;
1079
        dst_idx += cnt;
1080
1081
        /* get the chunk pointers */
1082
        srcp = get_ele_ptr(src_idx - 1, &src_avail) + 1;
1083
        dstp = get_ele_ptr(dst_idx - 1, &dst_avail) + 1;
1084
1085
        /* 
1086
         *   since we're working backwards, we actually want to know the
1087
         *   number of bytes on the page *before* the current pointers, so
1088
         *   subtract the available spaces from the size of the page to get
1089
         *   the available space preceding each 
1090
         */
1091
        src_avail = 32*1024 - src_avail + 1;
1092
        dst_avail = 32*1024 - dst_avail + 1;
1093
1094
        /* keep going until we've moved all of the requested bytes */
1095
        while (cnt != 0)
1096
        {
1097
            size_t move_len;
1098
1099
            /* 
1100
             *   figure the largest amount we can move - we can move the
1101
             *   smallest of the remaining requested move size, the source
1102
             *   chunk, and the destination chunk 
1103
             */
1104
            move_len = cnt;
1105
            if (move_len > src_avail)
1106
                move_len = src_avail;
1107
            if (move_len > dst_avail)
1108
                move_len = dst_avail;
1109
1110
            /* move the data */
1111
            memmove(dstp - move_len, srcp - move_len, move_len);
1112
1113
            /* advance all of the counters by the move size */
1114
            srcp -= move_len;
1115
            dstp -= move_len;
1116
            cnt -= move_len;
1117
            src_avail -= move_len;
1118
            dst_avail -= move_len;
1119
            src_idx -= move_len;
1120
            dst_idx -= move_len;
1121
1122
            /* stop if we're done */
1123
            if (cnt == 0)
1124
                break;
1125
1126
            /* if we've exhausted the source chunk, get the next one */
1127
            if (src_avail == 0)
1128
            {
1129
                srcp = get_ele_ptr(src_idx - 1, &src_avail) + 1;
1130
                src_avail = 32*1024 - src_avail + 1;
1131
            }
1132
1133
            /* if we've exhausted the destination chunk, get the next one */
1134
            if (dst_avail == 0)
1135
            {
1136
                dstp = get_ele_ptr(dst_idx - 1, &dst_avail) + 1;
1137
                dst_avail = 32*1024 - dst_avail + 1;
1138
            }
1139
        }
1140
    }
1141
}
1142
1143
/* ------------------------------------------------------------------------ */
1144
/*
1145
 *   Fill a 1-based index range with the given value 
1146
 */
1147
void CVmObjByteArray::fill_with(unsigned char val, unsigned long start_idx,
1148
                                unsigned long cnt)
1149
{
1150
    unsigned long idx;
1151
    unsigned long rem;
1152
    
1153
    /* ensure we don't overrun the array */
1154
    if (start_idx > get_element_count())
1155
        cnt = 0;
1156
    else if (start_idx + cnt - 1 > get_element_count())
1157
        cnt = get_element_count() + 1 - start_idx;
1158
1159
    /* continue until we exhaust the count */
1160
    for (idx = start_idx, rem = cnt ; rem != 0 ; )
1161
    {
1162
        size_t avail;
1163
        size_t chunk_size;
1164
        unsigned char *p;
1165
        
1166
        /* get the next chunk */
1167
        p = get_ele_ptr(idx, &avail);
1168
1169
        /* limit this chunk size to the remaining size to fill */
1170
        chunk_size = avail;
1171
        if (chunk_size > rem)
1172
            chunk_size = (size_t)rem;
1173
1174
        /* fill this chunk */
1175
        memset(p, val, chunk_size);
1176
1177
        /* skip this chunk */
1178
        rem -= chunk_size;
1179
        idx += chunk_size;
1180
    }
1181
}
1182
1183
/* ------------------------------------------------------------------------ */
1184
/* 
1185
 *   property evaluator - length 
1186
 */
1187
int CVmObjByteArray::getp_length(VMG_ vm_obj_id_t self,
1188
                                 vm_val_t *retval, uint *argc)
1189
{
1190
    static CVmNativeCodeDesc desc(0);
1191
1192
    /* check arguments */
1193
    if (get_prop_check_argc(retval, argc, &desc))
1194
        return TRUE;
1195
1196
    /* return the length */
1197
    retval->set_int(get_element_count());
1198
1199
    /* handled */
1200
    return TRUE;
1201
}
1202
1203
/* 
1204
 *   property evaluator - subarray 
1205
 */
1206
int CVmObjByteArray::getp_subarray(VMG_ vm_obj_id_t self,
1207
                                   vm_val_t *retval, uint *in_argc)
1208
{
1209
    uint argc = (in_argc != 0 ? *in_argc : 0);
1210
    static CVmNativeCodeDesc desc(1, 1);
1211
    unsigned long idx;
1212
    unsigned long cnt;
1213
    CVmObjByteArray *arr;
1214
    
1215
    /* check arguments */
1216
    if (get_prop_check_argc(retval, in_argc, &desc))
1217
        return TRUE;
1218
1219
    /* get the starting index */
1220
    idx = CVmBif::pop_int_val(vmg0_);
1221
1222
    /* force it to be in range */
1223
    if (idx < 1)
1224
        idx = 1;
1225
    else if (idx > get_element_count() + 1)
1226
        idx = get_element_count() + 1;
1227
1228
    /* if there's a count, get it */
1229
    if (argc >= 2)
1230
    {
1231
        /* get the explicit count */
1232
        cnt = CVmBif::pop_int_val(vmg0_);
1233
    }
1234
    else
1235
    {
1236
        /* use the entire rest of the array */
1237
        cnt = get_element_count();
1238
    }
1239
1240
    /* limit the count to the available size */
1241
    if (idx > get_element_count())
1242
        cnt = 0;
1243
    else if (idx + cnt - 1 > get_element_count())
1244
        cnt = get_element_count() + 1 - idx;
1245
1246
    /* push a self-reference while we're working for gc protection */
1247
    G_stk->push()->set_obj(self);
1248
1249
    /* allocate a new array to hold the result */
1250
    retval->set_obj(create(vmg_ FALSE, cnt));
1251
    arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj);
1252
1253
    /* copy the data from our array into the new one */
1254
    arr->copy_from(1, this, idx, cnt);
1255
1256
    /* discard our self-reference */
1257
    G_stk->discard();
1258
1259
    /* handled */
1260
    return TRUE;
1261
}
1262
1263
/* 
1264
 *   property evaluator - copy from another byte array 
1265
 */
1266
int CVmObjByteArray::getp_copy_from(VMG_ vm_obj_id_t self,
1267
                                    vm_val_t *retval, uint *argc)
1268
{
1269
    static CVmNativeCodeDesc desc(4);
1270
    unsigned long dst_idx;
1271
    unsigned long src_idx;
1272
    unsigned long cnt;
1273
    vm_obj_id_t src_arr_id;
1274
    CVmObjByteArray *src_arr;
1275
1276
    /* check arguments */
1277
    if (get_prop_check_argc(retval, argc, &desc))
1278
        return TRUE;
1279
1280
    /* get the source array */
1281
    src_arr_id = CVmBif::pop_obj_val(vmg0_);
1282
1283
    /* make sure it is indeed an array */
1284
    if (!is_byte_array(vmg_ src_arr_id))
1285
        err_throw(VMERR_BAD_TYPE_BIF);
1286
1287
    /* we know it's the right type, so cast it */
1288
    src_arr = (CVmObjByteArray *)vm_objp(vmg_ src_arr_id);
1289
1290
    /* get the starting source index */
1291
    src_idx = CVmBif::pop_int_val(vmg0_);
1292
1293
    /* force it to be in range */
1294
    if (src_idx < 1)
1295
        src_idx = 1;
1296
    else if (src_idx > src_arr->get_element_count() + 1)
1297
        src_idx = src_arr->get_element_count() + 1;
1298
1299
    /* get the destination index */
1300
    dst_idx = CVmBif::pop_int_val(vmg0_);
1301
1302
    /* force it to be within range */
1303
    if (dst_idx < 1)
1304
        dst_idx = 1;
1305
    else if (dst_idx > get_element_count() + 1)
1306
        dst_idx = get_element_count() + 1;
1307
1308
    /* get the count */
1309
    cnt = CVmBif::pop_int_val(vmg0_);
1310
1311
    /* limit the copying to the available destination space */
1312
    if (dst_idx > get_element_count())
1313
        cnt = 0;
1314
    else if (dst_idx + cnt - 1 > get_element_count())
1315
        cnt = get_element_count() + 1 - dst_idx;
1316
1317
    /* save undo for the change */
1318
    save_undo(vmg_ self, dst_idx, cnt);
1319
1320
    /* copy the data from the source array into our array */
1321
    copy_from(dst_idx, src_arr, src_idx, cnt);
1322
1323
    /* the result is 'self' */
1324
    retval->set_obj(self);
1325
1326
    /* handled */
1327
    return TRUE;
1328
}
1329
1330
/* 
1331
 *   property evaluator - fill with a value 
1332
 */
1333
int CVmObjByteArray::getp_fill_val(VMG_ vm_obj_id_t self,
1334
                                   vm_val_t *retval, uint *in_argc)
1335
{
1336
    uint argc = (in_argc != 0 ? *in_argc : 0);
1337
    static CVmNativeCodeDesc desc(1, 2);
1338
    unsigned long idx;
1339
    unsigned long cnt;
1340
    long fill_val;
1341
1342
    /* check arguments */
1343
    if (get_prop_check_argc(retval, in_argc, &desc))
1344
        return TRUE;
1345
1346
    /* get the value with which to fill elements */
1347
    fill_val = CVmBif::pop_int_val(vmg0_);
1348
    if (fill_val < 0 || fill_val > 255)
1349
        err_throw(VMERR_OUT_OF_RANGE);
1350
1351
    /* get the starting index, if provided */
1352
    if (argc >= 2)
1353
    {
1354
        /* get the index */
1355
        idx = CVmBif::pop_int_val(vmg0_);
1356
1357
        /* force it to be in range */
1358
        if (idx < 1)
1359
            idx = 1;
1360
        else if (idx > get_element_count() + 1)
1361
            idx = get_element_count() + 1;
1362
    }
1363
    else
1364
    {
1365
        /* fill from the first element */
1366
        idx = 1;
1367
    }
1368
1369
    /* get the count, if provided */
1370
    if (argc >= 3)
1371
    {
1372
        /* get the count */
1373
        cnt = CVmBif::pop_int_val(vmg0_);
1374
    }
1375
    else
1376
    {
1377
        /* fill the entire array */
1378
        cnt = get_element_count();
1379
    }
1380
1381
    /* force the length to be in range */
1382
    if (idx > get_element_count())
1383
        cnt = 0;
1384
    else if (idx + cnt - 1 > get_element_count())
1385
        cnt = get_element_count() + 1 - idx;
1386
1387
    /* save undo for the change */
1388
    save_undo(vmg_ self, idx, cnt);
1389
1390
    /* fill with the given value */
1391
    fill_with((unsigned char)fill_val, idx, cnt);
1392
1393
    /* the result is 'self' */
1394
    retval->set_obj(self);
1395
1396
    /* handled */
1397
    return TRUE;
1398
}
1399
1400
/* 
1401
 *   property evaluator - convert to string 
1402
 */
1403
int CVmObjByteArray::getp_to_string(VMG_ vm_obj_id_t self,
1404
                                    vm_val_t *retval, uint *in_argc)
1405
{
1406
    uint argc = (in_argc != 0 ? *in_argc : 0);
1407
    static CVmNativeCodeDesc desc(1, 2);
1408
    unsigned long idx;
1409
    unsigned long cnt;
1410
    vm_obj_id_t charset_id;
1411
    CCharmapToUni *mapper;
1412
    size_t str_len;
1413
    CVmObjString *str;
1414
1415
    /* check arguments */
1416
    if (get_prop_check_argc(retval, in_argc, &desc))
1417
        return TRUE;
1418
1419
    /* get the character set object */
1420
    charset_id = CVmBif::pop_obj_val(vmg0_);
1421
1422
    /* make sure it's really a character set object */
1423
    if (!CVmObjCharSet::is_charset(vmg_ charset_id))
1424
        err_throw(VMERR_BAD_TYPE_BIF);
1425
1426
    /* get the to-unicode mapping object from the character set */
1427
    mapper = ((CVmObjCharSet *)vm_objp(vmg_ charset_id))->get_to_uni(vmg0_);
1428
1429
    /* if there's a starting index, retrieve it */
1430
    if (argc >= 2)
1431
    {
1432
        /* retrieve the starting index */
1433
        idx = CVmBif::pop_int_val(vmg0_);
1434
1435
        /* force it to be in range */
1436
        if (idx < 1)
1437
            idx = 1;
1438
        else if (idx > get_element_count() + 1)
1439
            idx = get_element_count() + 1;
1440
    }
1441
    else
1442
    {
1443
        /* start at the first byte */
1444
        idx = 1;
1445
    }
1446
1447
    /* if there's a length, retrieve it */
1448
    if (argc >= 3)
1449
    {
1450
        /* retrieve the length */
1451
        cnt = CVmBif::pop_int_val(vmg0_);
1452
    }
1453
    else
1454
    {
1455
        /* use all remaining characters */
1456
        cnt = get_element_count();
1457
    }
1458
1459
    /* force the length to be in range */
1460
    if (idx > get_element_count())
1461
        cnt = 0;
1462
    else if (idx + cnt - 1 > get_element_count())
1463
        cnt = get_element_count() + 1 - idx;
1464
1465
    /* 
1466
     *   map to a string without a destination buffer, to determine the
1467
     *   required length to store the string 
1468
     */
1469
    str_len = map_to_string(idx, cnt, 0, 0, mapper);
1470
1471
    /* push a self-ref for gc protection */
1472
    G_stk->push()->set_obj(self);
1473
1474
    /* push a ref to the character mapper as well */
1475
    G_stk->push()->set_obj(charset_id);
1476
1477
    /* allocate a string of the required length */
1478
    retval->set_obj(CVmObjString::create(vmg_ FALSE, str_len));
1479
    str = (CVmObjString *)vm_objp(vmg_ retval->val.obj);
1480
1481
    /* map the string, actually storing the bytes this time */
1482
    map_to_string(idx, cnt, str, str_len, mapper);
1483
1484
    /* discard the gc protection */
1485
    G_stk->discard(2);
1486
    
1487
    /* handled */
1488
    return TRUE;
1489
}
1490
1491
/*
1492
 *   Run a range of bytes through a character mapper to produce string data,
1493
 *   optionally storing the string data in a string object.  Returns the
1494
 *   number of bytes in the resulting string.  
1495
 */
1496
size_t CVmObjByteArray::map_to_string(unsigned long idx,
1497
                                      unsigned long len,
1498
                                      CVmObjString *str, size_t str_len,
1499
                                      CCharmapToUni *mapper)
1500
{
1501
    size_t str_total;
1502
    size_t buf_len;
1503
    unsigned char buf[128];
1504
    char *dst;
1505
1506
    /* get the string's buffer pointer */
1507
    dst = (str != 0 ? str->cons_get_buf() : 0);
1508
    
1509
    /* go through the source bytes a bit at a time */
1510
    for (str_total = 0, buf_len = 0 ; len != 0 ; )
1511
    {
1512
        size_t copy_len;
1513
        size_t str_cur;
1514
        size_t partial_len;
1515
        
1516
        /* 
1517
         *   fill up the buffer, up to the remaining source length or the
1518
         *   buffer's capacity, whichever is lower 
1519
         */
1520
        copy_len = sizeof(buf) - buf_len;
1521
        if (copy_len > len)
1522
            copy_len = (size_t)len;
1523
1524
        /* copy the bytes to our staging buffer */
1525
        copy_to_buf(buf, idx, copy_len);
1526
1527
        /* add the copied bytes into the buffer length */
1528
        buf_len += copy_len;
1529
1530
        /* advance past the copied bytes in the source */
1531
        idx += copy_len;
1532
        len -= copy_len;
1533
1534
        /* translate the bytes through the character set mapping */
1535
        str_cur = mapper->map2(&dst, &str_len, (char *)buf, buf_len,
1536
                               &partial_len);
1537
1538
        /* 
1539
         *   if this would push us over the maximum string size, we can't
1540
         *   convert the data 
1541
         */
1542
        if (str_cur > OSMALMAX - str_total - VMB_LEN)
1543
            err_throw(VMERR_OUT_OF_MEMORY);
1544
1545
        /* add the current length into the total string length */
1546
        str_total += str_cur;
1547
1548
        /* copy the partial last character bytes to the start of the buffer */
1549
        if (partial_len != 0)
1550
            memmove(buf, buf + buf_len - partial_len, partial_len);
1551
1552
        /* the buffer now contains only the partial character bytes */
1553
        buf_len = partial_len;
1554
    }
1555
1556
    /* return the total string length */
1557
    return str_total;
1558
}
1559
1560
/*
1561
 *   Copy bytes from the array to a buffer 
1562
 */
1563
void CVmObjByteArray::copy_to_buf(unsigned char *buf,
1564
                                  unsigned long idx, size_t len) const
1565
{
1566
    /* keep going until we satisfy the request */
1567
    while (len != 0)
1568
    {
1569
        size_t avail;
1570
        unsigned char *p;
1571
        size_t copy_len;
1572
1573
        /* get the next chunk */
1574
        p = get_ele_ptr(idx, &avail);
1575
1576
        /* copy the available bytes or the reamining desired bytes */
1577
        copy_len = avail;
1578
        if (copy_len > len)
1579
            copy_len = len;
1580
1581
        /* copy the bytes */
1582
        memcpy(buf, p, copy_len);
1583
1584
        /* advance past the copied bytes */
1585
        buf += copy_len;
1586
        idx += copy_len;
1587
        len -= copy_len;
1588
    }
1589
}
1590
1591
/*
1592
 *   Copy bytes from a buffer into the array
1593
 */
1594
void CVmObjByteArray::copy_from_buf(const unsigned char *buf,
1595
                                    unsigned long idx, size_t len)
1596
{
1597
    /* keep going until we satisfy the request */
1598
    while (len != 0)
1599
    {
1600
        size_t avail;
1601
        unsigned char *p;
1602
        size_t copy_len;
1603
1604
        /* get the next chunk */
1605
        p = get_ele_ptr(idx, &avail);
1606
1607
        /* copy the available bytes or the reamining desired bytes */
1608
        copy_len = avail;
1609
        if (copy_len > len)
1610
            copy_len = len;
1611
1612
        /* copy the bytes */
1613
        memcpy(p, buf, copy_len);
1614
1615
        /* advance past the copied bytes */
1616
        buf += copy_len;
1617
        idx += copy_len;
1618
        len -= copy_len;
1619
    }
1620
}
1621
1622
/* ------------------------------------------------------------------------ */
1623
/* 
1624
 *   property evaluator - read an integer 
1625
 */
1626
int CVmObjByteArray::getp_read_int(VMG_ vm_obj_id_t self,
1627
                                   vm_val_t *retval, uint *argc)
1628
{
1629
    static CVmNativeCodeDesc desc(2);
1630
    unsigned int idx;
1631
    unsigned int fmt;
1632
    long result = 0;
1633
    size_t siz;
1634
    unsigned char cbuf[4];
1635
1636
    /* check arguments */
1637
    if (get_prop_check_argc(retval, argc, &desc))
1638
        return TRUE;
1639
1640
    /* get the starting index and format code */
1641
    idx = CVmBif::pop_int_val(vmg0_);
1642
    fmt = CVmBif::pop_int_val(vmg0_);
1643
1644
    /* get the size from the format */
1645
    switch (fmt & FmtSizeMask)
1646
    {
1647
    case FmtInt8:
1648
    default:
1649
        siz = 1;
1650
        break;
1651
1652
    case FmtInt16:
1653
        siz = 2;
1654
        break;
1655
1656
    case FmtInt32:
1657
        siz = 4;
1658
        break;
1659
    }
1660
1661
    /* check that the index is in range */
1662
    if (idx < 1 || idx + siz - 1 > get_element_count())
1663
        err_throw(VMERR_INDEX_OUT_OF_RANGE);
1664
1665
    /* make a copy of the bytes in cbuf[] for easy access */
1666
    copy_to_buf(cbuf, idx, siz);
1667
1668
    /* read the value */
1669
    switch (siz)
1670
    {
1671
    case 1:
1672
        /* 8-bit integer - all that matters is the signedness */
1673
        if ((fmt & FmtSignedMask) == FmtSigned)
1674
            result = (long)(int)(char)cbuf[0];
1675
        else
1676
            result = cbuf[0];
1677
        break;
1678
1679
    case 2:
1680
        /* 
1681
         *   16-bit integer.  First, pull out an unsigned 16-bit value using
1682
         *   the selected byte order.  
1683
         */
1684
        switch(fmt & FmtOrderMask)
1685
        {
1686
        case FmtLittleEndian:
1687
        default:
1688
            result = cbuf[0]
1689
                     + (((unsigned int)cbuf[1]) << 8);
1690
            break;
1691
1692
        case FmtBigEndian:
1693
            result = (((unsigned int)cbuf[0]) << 8)
1694
                     + cbuf[1];
1695
            break;
1696
        }
1697
1698
        /* 
1699
         *   Now make it a signed value if appropriate.  To ensure we get the
1700
         *   proper results regardless of local data sizes, we'll write it
1701
         *   back to the buffer in portable format, then use the OS-defined
1702
         *   signed 16-bit extraction macro to convert it back to a signed
1703
         *   value.  
1704
         */
1705
        if ((fmt & FmtSignedMask) == FmtSigned)
1706
        {
1707
            oswp2(cbuf, result);
1708
            result = osrp2s(cbuf);
1709
        }
1710
        break;
1711
1712
    case 4:
1713
        /* 
1714
         *   32-bit integer.  Pull out a signed 32-bit value using the
1715
         *   selected byte order.  Since we can't represent a 32-bit unsigned
1716
         *   value in the VM, we can ignore the signedness format.  
1717
         */
1718
        switch(fmt & FmtOrderMask)
1719
        {
1720
        case FmtLittleEndian:
1721
        default:
1722
            result = ((unsigned long)cbuf[0])
1723
                     + (((unsigned long)cbuf[1]) << 8)
1724
                     + (((unsigned long)cbuf[2]) << 16)
1725
                     + (((unsigned long)cbuf[3]) << 24);
1726
            break;
1727
1728
        case FmtBigEndian:
1729
            result = (((unsigned long)cbuf[0]) << 24)
1730
                     + (((unsigned long)cbuf[1]) << 16)
1731
                     + (((unsigned long)cbuf[2]) << 8)
1732
                     + ((unsigned long)cbuf[3]);
1733
            break;
1734
        }
1735
    }
1736
1737
    /* return the result */
1738
    retval->set_int(result);
1739
1740
    /* handled */
1741
    return TRUE;
1742
}
1743
1744
/* 
1745
 *   property evaluator - write an integer 
1746
 */
1747
int CVmObjByteArray::getp_write_int(VMG_ vm_obj_id_t self,
1748
                                    vm_val_t *retval, uint *argc)
1749
{
1750
    static CVmNativeCodeDesc desc(3);
1751
    unsigned int idx;
1752
    unsigned int fmt;
1753
    long val;
1754
    size_t siz;
1755
    unsigned char cbuf[4];
1756
1757
    /* check arguments */
1758
    if (get_prop_check_argc(retval, argc, &desc))
1759
        return TRUE;
1760
1761
    /* get the starting index, format code, and value to write */
1762
    idx = CVmBif::pop_int_val(vmg0_);
1763
    fmt = CVmBif::pop_int_val(vmg0_);
1764
    val = CVmBif::pop_long_val(vmg0_);
1765
1766
    /* get the size from the format */
1767
    switch (fmt & FmtSizeMask)
1768
    {
1769
    case FmtInt8:
1770
    default:
1771
        siz = 1;
1772
        break;
1773
1774
    case FmtInt16:
1775
        siz = 2;
1776
        break;
1777
1778
    case FmtInt32:
1779
        siz = 4;
1780
        break;
1781
    }
1782
1783
    /* check that the index is in range */
1784
    if (idx < 1 || idx + siz - 1 > get_element_count())
1785
        err_throw(VMERR_INDEX_OUT_OF_RANGE);
1786
1787
    /* write the value to cbuf[] */
1788
    switch (siz)
1789
    {
1790
    case 1:
1791
        /* 8-bit integer */
1792
        cbuf[0] = (char)(val & 0xFF);
1793
        break;
1794
1795
    case 2:
1796
        /* 16-bit integer - store in the proper byte order */
1797
        switch(fmt & FmtOrderMask)
1798
        {
1799
        case FmtLittleEndian:
1800
        default:
1801
            cbuf[0] = (char)(val & 0xFF);
1802
            cbuf[1] = (char)((val & 0xFF00) >> 8);
1803
            break;
1804
1805
        case FmtBigEndian:
1806
            cbuf[0] = (char)((val & 0xFF00) >> 8);
1807
            cbuf[1] = (char)(val & 0xFF);
1808
            break;
1809
        }
1810
        break;
1811
1812
    case 4:
1813
        /* 32-bit integer - store in the proper byte order */
1814
        switch(fmt & FmtOrderMask)
1815
        {
1816
        case FmtLittleEndian:
1817
        default:
1818
            cbuf[0] = (char)(val & 0xFF);
1819
            cbuf[1] = (char)((val & 0xFF00) >> 8);
1820
            cbuf[2] = (char)((val & 0xFF0000) >> 16);
1821
            cbuf[3] = (char)((val & 0xFF000000) >> 24);
1822
            break;
1823
1824
        case FmtBigEndian:
1825
            cbuf[0] = (char)((val & 0xFF000000) >> 24);
1826
            cbuf[1] = (char)((val & 0xFF0000) >> 16);
1827
            cbuf[2] = (char)((val & 0xFF00) >> 8);
1828
            cbuf[3] = (char)(val & 0xFF);
1829
            break;
1830
        }
1831
    }
1832
1833
    /* store the byte representation we've constructed */
1834
    copy_from_buf(cbuf, idx, siz);
1835
    
1836
    /* there's no return value */
1837
    retval->set_nil();
1838
1839
    /* handled */
1840
    return TRUE;
1841
}
1842
1843
1844
1845
/* ------------------------------------------------------------------------ */
1846
/*
1847
 *   Write bytes from the specified region of the array to a file.  Returns
1848
 *   zero on success, non-zero on failure.  
1849
 */
1850
int CVmObjByteArray::write_to_file(osfildef *fp, unsigned long start_idx,
1851
                                   unsigned long len) const
1852
{
1853
    unsigned long rem;
1854
    unsigned long idx;
1855
1856
    /* make sure the starting index is valid */
1857
    if (start_idx < 1)
1858
        err_throw(VMERR_INDEX_OUT_OF_RANGE);
1859
    
1860
    /* 
1861
     *   if the starting index is past the end of the array, there's nothing
1862
     *   to do - just return success
1863
     */
1864
    if (start_idx > get_element_count())
1865
        return 0;
1866
1867
    /* 
1868
     *   limit the request to the number of bytes available after the
1869
     *   starting index 
1870
     */
1871
    if (start_idx + len - 1 > get_element_count())
1872
        len = get_element_count() - start_idx + 1;
1873
1874
    /* keep going until we satisfy the request or run into a problem */
1875
    for (idx = start_idx, rem = len ; rem != 0 ; )
1876
    {
1877
        unsigned char *p;
1878
        size_t avail;
1879
        size_t chunk;
1880
        
1881
        /* get the next chunk */
1882
        p = get_ele_ptr(idx, &avail);
1883
1884
        /* limit writing to the amount remaining of the requested size */
1885
        chunk = avail;
1886
        if (chunk > rem)
1887
            chunk = (size_t)rem;
1888
1889
        /* 
1890
         *   write out this chunk - if an error occurs, abort with a failure
1891
         *   indication 
1892
         */
1893
        if (osfwb(fp, p, chunk))
1894
            return 1;
1895
1896
        /* move our counters past this chunk */
1897
        idx += chunk;
1898
        rem -= chunk;
1899
    }
1900
1901
    /* we satisfied the request without problems - indicate success */
1902
    return 0;
1903
}
1904
1905
/*
1906
 *   Read bytes from the file into the specified region of the array.
1907
 *   Returns the number of bytes actually read.  
1908
 */
1909
unsigned long CVmObjByteArray::read_from_file(osfildef *fp,
1910
                                              unsigned long start_idx,
1911
                                              unsigned long len)
1912
{
1913
    unsigned long rem;
1914
    unsigned long idx;
1915
    unsigned long total;
1916
1917
    /* 
1918
     *   if the starting index is past the end of the array, there's nothing
1919
     *   to do - just return success 
1920
     */
1921
    if (start_idx > get_element_count())
1922
        return 0;
1923
1924
    /* 
1925
     *   limit the request to the number of bytes available after the
1926
     *   starting index 
1927
     */
1928
    if (start_idx + len - 1 > get_element_count())
1929
        len = get_element_count() - start_idx + 1;
1930
1931
    /* keep going until we satisfy the request or run into a problem */
1932
    for (idx = start_idx, rem = len, total = 0 ; rem != 0 ; )
1933
    {
1934
        unsigned char *p;
1935
        size_t avail;
1936
        size_t chunk;
1937
        size_t cur_read;
1938
1939
        /* get the next chunk */
1940
        p = get_ele_ptr(idx, &avail);
1941
1942
        /* limit reading to the amount of the request remaining */
1943
        chunk = avail;
1944
        if (chunk > rem)
1945
            chunk = (size_t)rem;
1946
1947
        /* read as much as we can of this chunk */
1948
        cur_read = osfrbc(fp, p, chunk);
1949
1950
        /* add this amount into the total so far */
1951
        total += cur_read;
1952
1953
        /* 
1954
         *   if we didn't get as much as we asked for, we must have reached
1955
         *   the end of the file before satisfying the request - there's
1956
         *   nothing more to be read in this case, so we can stop looping 
1957
         */
1958
        if (cur_read != chunk)
1959
            break;
1960
1961
        /* move our counters past this chunk */
1962
        idx += chunk;
1963
        rem -= chunk;
1964
    }
1965
1966
    /* return the total amount we read */
1967
    return total;
1968
}
1969
1970
/* ------------------------------------------------------------------------ */
1971
/*
1972
 *   Write to a 'data' mode file 
1973
 */
1974
int CVmObjByteArray::write_to_data_file(osfildef *fp)
1975
{
1976
    char buf[16];
1977
1978
    /* write the number of bytes in our array */
1979
    oswp4(buf, get_element_count());
1980
    if (osfwb(fp, buf, 4))
1981
        return 1;
1982
1983
    /* write the bytes */
1984
    return write_to_file(fp, 1, get_element_count());
1985
}
1986
1987
/*
1988
 *   Read from a 'data' mode file 
1989
 */
1990
int CVmObjByteArray::read_from_data_file(VMG_ vm_val_t *retval, osfildef *fp)
1991
{
1992
    char buf[16];
1993
    CVmObjByteArray *arr;
1994
    unsigned long len;
1995
1996
    /* read the number of bytes in the array */
1997
    if (osfrb(fp, buf, 4))
1998
        return 1;
1999
    len = t3rp4u(buf);
2000
2001
    /* create a new ByteArray to hold the result */
2002
    retval->set_obj(create(vmg_ FALSE, len));
2003
    arr = (CVmObjByteArray *)vm_objp(vmg_ retval->val.obj);
2004
2005
    /* read the bytes */
2006
    if (arr->read_from_file(fp, 1, len) != len)
2007
    {
2008
        /* 
2009
         *   we didn't manage to read all of the bytes - since the value was
2010
         *   tagged with the correct number of bytes, end-of-file in the
2011
         *   middle of the bytes indicates a corrupted file, so return
2012
         *   failure 
2013
         */
2014
        return 1;
2015
    }
2016
2017
    /* success */
2018
    return 0;
2019
}
2020