Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/containers/string_res.cfa

    r6cc87c0 r97c215f  
    1515
    1616#include "string_res.hfa"
     17#include "string_sharectx.hfa"
     18
    1719#include <stdlib.hfa>  // e.g. malloc
    18 #include <string.h>    // e.g. strlen
     20#include <assert.h>
    1921
    2022//######################### VbyteHeap "header" #########################
    21 
    22 
    23 
    24 
    25 
    26 
    27 
    28 
    29 // DON'T COMMIT:
    30 // #define VbyteDebug
    31 
    32 
    33 
    34 
    3523
    3624#ifdef VbyteDebug
     
    5442
    5543   
    56 static inline void compaction( VbyteHeap & );                           // compaction of the byte area
    57 static inline void garbage( VbyteHeap & );                              // garbage collect the byte area
    58 static inline void extend( VbyteHeap &, int );                  // extend the size of the byte area
    59 static inline void reduce( VbyteHeap &, int );                  // reduce the size of the byte area
    60 
    61 static inline void ?{}( VbyteHeap &, int = 1000 );
    62 static inline void ^?{}( VbyteHeap & );
    63 static inline void ByteCopy( VbyteHeap &, char *, int, int, char *, int, int ); // copy a block of bytes from one location in the heap to another
    64 static inline int ByteCmp( VbyteHeap &, char *, int, int, char *, int, int );   // compare 2 blocks of bytes
    65 static inline char *VbyteAlloc( VbyteHeap &, int );                     // allocate a block bytes in the heap
    66 
    67 
    68 static inline void AddThisAfter( HandleNode &, HandleNode & );
    69 static inline void DeleteNode( HandleNode & );
    70 static inline void MoveThisAfter( HandleNode &, const HandleNode & );           // move current handle after parameter handle
     44static void compaction( VbyteHeap & );                          // compaction of the byte area
     45static void garbage( VbyteHeap &, int );                                // garbage collect the byte area
     46static void extend( VbyteHeap &, int );                 // extend the size of the byte area
     47static void reduce( VbyteHeap &, int );                 // reduce the size of the byte area
     48
     49static void ?{}( VbyteHeap &, size_t = 1000 );
     50static void ^?{}( VbyteHeap & );
     51
     52static int ByteCmp( char *, int, int, char *, int, int );       // compare 2 blocks of bytes
     53static char *VbyteAlloc( VbyteHeap &, int );                    // allocate a block bytes in the heap
     54static char *VbyteTryAdjustLast( VbyteHeap &, int );
     55
     56static void AddThisAfter( HandleNode &, HandleNode & );
     57static void DeleteNode( HandleNode & );
     58static void MoveThisAfter( HandleNode &, const HandleNode & );          // move current handle after parameter handle
    7159
    7260
    7361// Allocate the storage for the variable sized area and intialize the heap variables.
    7462
    75 static inline void ?{}( VbyteHeap & this, int Size ) with(this) {
     63static void ?{}( VbyteHeap & this, size_t Size ) with(this) {
    7664#ifdef VbyteDebug
    7765    serr | "enter:VbyteHeap::VbyteHeap, this:" | &this | " Size:" | Size;
     
    8270    ExtVbyte = (void *)( StartVbyte + CurrSize );
    8371    Header.flink = Header.blink = &Header;
     72    Header.ulink = & this;
    8473#ifdef VbyteDebug
    8574    HeaderPtr = &Header;
     
    9180// Release the dynamically allocated storage for the byte area.
    9281
    93 static inline void ^?{}( VbyteHeap & this ) with(this) {
     82static void ^?{}( VbyteHeap & this ) with(this) {
    9483    free( StartVbyte );
    9584} // ~VbyteHeap
     
    10291// creator.
    10392
    104 void ?{}( HandleNode & this ) with(this) {
     93static void ?{}( HandleNode & this ) with(this) {
    10594#ifdef VbyteDebug
    10695    serr | "enter:HandleNode::HandleNode, this:" | &this;
     
    117106// collection.
    118107
    119 void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
     108static void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
    120109#ifdef VbyteDebug
    121110    serr | "enter:HandleNode::HandleNode, this:" | &this;
     
    123112    s = 0;
    124113    lnth = 0;
     114    ulink = &vh;
    125115    AddThisAfter( this, *vh.Header.blink );
    126116#ifdef VbyteDebug
     
    133123// is the responsibility of the creator to destroy it.
    134124
    135 void ^?{}( HandleNode & this ) with(this) {
     125static void ^?{}( HandleNode & this ) with(this) {
    136126#ifdef VbyteDebug
    137127    serr | "enter:HandleNode::~HandleNode, this:" | & this;
     
    149139} // ~HandleNode
    150140
     141
     142//######################### String Sharing Context #########################
     143
     144static string_sharectx * ambient_string_sharectx;               // fickle top of stack
     145static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack
     146
     147void ?{}( string_sharectx & this, StringSharectx_Mode mode ) with( this ) {
     148    (older){ ambient_string_sharectx };
     149    if ( mode == NEW_SHARING ) {
     150        (activeHeap){ new( (size_t) 1000 ) };
     151    } else {
     152        verify( mode == NO_SHARING );
     153        (activeHeap){ 0p };
     154    }
     155    ambient_string_sharectx = & this;
     156}
     157
     158void ^?{}( string_sharectx & this ) with( this ) {
     159    if ( activeHeap ) delete( activeHeap );
     160
     161    // unlink this from older-list starting from ambient_string_sharectx
     162    // usually, this==ambient_string_sharectx and the loop runs zero times
     163    string_sharectx *& c = ambient_string_sharectx;
     164    while ( c != &this ) &c = &c->older;              // find this
     165    c = this.older;                                   // unlink
     166}
     167
    151168//######################### String Resource #########################
    152169
    153170
    154 VbyteHeap HeapArea;
    155 
    156 VbyteHeap * DEBUG_string_heap = & HeapArea;
     171VbyteHeap * DEBUG_string_heap() {
     172    assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
     173    return ambient_string_sharectx->activeHeap;
     174}
    157175
    158176size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) {
     
    160178}
    161179
     180size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) {
     181    return heap->CurrSize;
     182}
     183
    162184const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
    163185    return heap->StartVbyte;
    164186}
    165 
    166187
    167188// Returns the size of the string in bytes
     
    187208    // Store auto-newline state so it can be restored
    188209    bool anl = getANL$(out);
    189     nlOff(out);
    190     for (size_t i = 0; i < s.Handle.lnth; i++) {
    191         // Need to re-apply on the last output operator, for whole-statement version
    192         if (anl && i == s.Handle.lnth-1) nlOn(out);
    193         out | s[i];
    194     }
    195     return out;
     210    if( s.Handle.lnth == 0 ) {
     211        sout | "";
     212    } else {
     213        nlOff(out);
     214        for (size_t i = 0; i < s.Handle.lnth; i++) {
     215            // Need to re-apply on the last output operator, for whole-statement version
     216            if (anl && i == s.Handle.lnth-1) nlOn(out);
     217            out | s[i];
     218        }
     219    }
    196220}
    197221
    198222// Empty constructor
    199223void ?{}(string_res &s) with(s) {
    200     (Handle){ HeapArea };
     224    if( ambient_string_sharectx->activeHeap ) {
     225        (Handle){ * ambient_string_sharectx->activeHeap };
     226        (shareEditSet_owns_ulink){ false };
     227        verify( Handle.s == 0p && Handle.lnth == 0 );
     228    } else {
     229        (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
     230        (shareEditSet_owns_ulink){ true };
     231        Handle.s = Handle.ulink->StartVbyte;
     232        verify( Handle.lnth == 0 );
     233    }
    201234    s.shareEditSet_prev = &s;
    202235    s.shareEditSet_next = &s;
    203236}
    204237
    205 // Constructor from a raw buffer and size
    206 void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
    207     (Handle){ HeapArea };
    208     Handle.s = VbyteAlloc(HeapArea, rhslnth);
     238static void eagerCopyCtorHelper(string_res &s, const char* rhs, size_t rhslnth) with(s) {
     239    if( ambient_string_sharectx->activeHeap ) {
     240        (Handle){ * ambient_string_sharectx->activeHeap };
     241        (shareEditSet_owns_ulink){ false };
     242    } else {
     243        (Handle){ * new( rhslnth ) };
     244        (shareEditSet_owns_ulink){ true };
     245    }
     246    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    209247    Handle.lnth = rhslnth;
    210248    for ( int i = 0; i < rhslnth; i += 1 ) {            // copy characters
     
    215253}
    216254
    217 // String literal constructor
    218 void ?{}(string_res &s, const char* rhs) {
    219     (s){ rhs, strlen(rhs) };
     255// Constructor from a raw buffer and size
     256void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
     257    eagerCopyCtorHelper(s, rhs, rhslnth);
     258}
     259
     260// private ctor (not in header): use specified heap (ignore ambient) and copy chars in
     261void ?{}( string_res &s, VbyteHeap & heap, const char* rhs, size_t rhslnth ) with(s) {
     262    (Handle){ heap };
     263    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     264    Handle.lnth = rhslnth;
     265    (s.shareEditSet_owns_ulink){ false };
     266    for ( int i = 0; i < rhslnth; i += 1 ) {            // copy characters
     267        Handle.s[i] = rhs[i];
     268    } // for
     269    s.shareEditSet_prev = &s;
     270    s.shareEditSet_next = &s;
    220271}
    221272
     
    223274void ?{}(string_res &s, const string_res & s2, StrResInitMode mode, size_t start, size_t end ) {
    224275
    225     (s.Handle){ HeapArea };
    226     s.Handle.s = s2.Handle.s + start;
    227     s.Handle.lnth = end - start;
    228     MoveThisAfter(s.Handle, s2.Handle );                        // insert this handle after rhs handle
    229     // ^ bug?  skip others at early point in string
    230    
    231     if (mode == COPY_VALUE) {
    232         // make s alone in its shareEditSet
    233         s.shareEditSet_prev = &s;
    234         s.shareEditSet_next = &s;
     276    verify( start <= end && end <= s2.Handle.lnth );
     277
     278    if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
     279        // crossing heaps (including private): copy eagerly
     280        eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
     281        verify(s.shareEditSet_prev == &s);
     282        verify(s.shareEditSet_next == &s);
    235283    } else {
    236         assert( mode == SHARE_EDITS );
    237 
    238         // s2 is logically const but not implementation const
    239         string_res & s2mod = (string_res &) s2;
    240 
    241         // insert s after s2 on shareEditSet
    242         s.shareEditSet_next = s2mod.shareEditSet_next;
    243         s.shareEditSet_prev = &s2mod;
    244         s.shareEditSet_next->shareEditSet_prev = &s;
    245         s.shareEditSet_prev->shareEditSet_next = &s;
    246     }
    247 }
    248 
    249 void assign(string_res &this, const char* buffer, size_t bsize) {
    250 
    251     // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
    252     string_res * shareEditSetStartPeer = & this;
    253     string_res * shareEditSetEndPeer = & this;
    254     for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
    255         if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
    256             shareEditSetStartPeer = editPeer;
     284        (s.Handle){};
     285        s.Handle.s = s2.Handle.s + start;
     286        s.Handle.lnth = end - start;
     287        s.Handle.ulink = s2.Handle.ulink;
     288
     289        AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
     290        // ^ bug?  skip others at early point in string
     291
     292        if (mode == COPY_VALUE) {
     293            verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
     294            // requested logical copy in same heap: defer copy until write
     295
     296            (s.shareEditSet_owns_ulink){ false };
     297
     298            // make s alone in its shareEditSet
     299            s.shareEditSet_prev = &s;
     300            s.shareEditSet_next = &s;
     301        } else {
     302            verify( mode == SHARE_EDITS );
     303            // sharing edits with source forces same heap as source (ignore context)
     304
     305            (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
     306
     307            // s2 is logically const but not implementation const
     308            string_res & s2mod = (string_res &) s2;
     309
     310            // insert s after s2 on shareEditSet
     311            s.shareEditSet_next = s2mod.shareEditSet_next;
     312            s.shareEditSet_prev = &s2mod;
     313            s.shareEditSet_next->shareEditSet_prev = &s;
     314            s.shareEditSet_prev->shareEditSet_next = &s;
    257315        }
    258         if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
    259             shareEditSetEndPeer = editPeer;
    260         }
    261     }
    262 
    263     // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
    264     // `this` occurs in the middle of it, to be replaced
    265     // build up the new text in `pasting`
    266 
    267     string_res pasting = {
    268         shareEditSetStartPeer->Handle.s,                   // start of SES
    269         this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
    270     append( pasting,
    271         buffer,                                            // start of replacement for this
    272         bsize );                                           // length of replacement for this
    273     append( pasting,
    274         this.Handle.s + this.Handle.lnth,                  // start of SES after this
    275         shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
    276         (this.Handle.s + this.Handle.lnth) );              // length of SES, after this
    277 
    278     // The above string building can trigger compaction.
    279     // The reference points (that are arguments of the string building) may move during that building.
    280     // From this point on, they are stable.
    281     // So now, capture their values for use in the overlap cases, below.
    282     // Do not factor these definitions with the arguments used above.
     316    }
     317}
     318
     319static void assignEditSet(string_res & this, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
     320    char * resultSesStart,
     321    size_t resultSesLnth,
     322    HandleNode * resultPadPosition, size_t bsize ) {
    283323
    284324    char * beforeBegin = shareEditSetStartPeer->Handle.s;
     
    290330    size_t oldLnth = this.Handle.lnth;
    291331
    292     this.Handle.s = pasting.Handle.s + beforeLen;
     332    this.Handle.s = resultSesStart + beforeLen;
    293333    this.Handle.lnth = bsize;
    294     MoveThisAfter( this.Handle, pasting.Handle );
     334    if (resultPadPosition)
     335        MoveThisAfter( this.Handle, *resultPadPosition );
    295336
    296337    // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
    297     char *limit = pasting.Handle.s + pasting.Handle.lnth;
     338    char *limit = resultSesStart + resultSesLnth;
    298339    for (string_res * p = this.shareEditSet_next; p != &this; p = p->shareEditSet_next) {
    299         assert (p->Handle.s >= beforeBegin);
     340        verify (p->Handle.s >= beforeBegin);
    300341        if ( p->Handle.s >= afterBegin ) {
    301             assert ( p->Handle.s <= afterBegin + afterLen );
    302             assert ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     342            verify ( p->Handle.s <= afterBegin + afterLen );
     343            verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    303344            // p starts after the edit
    304345            // take start and end as end-anchored
     
    318359            } else {
    319360                // p ends after the edit
    320                 assert ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     361                verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    321362                // take end as end-anchored
    322363                // stretch-shrink p according to the edit
     
    326367            // take start as start-anchored
    327368            size_t startOffsetFromStart = p->Handle.s - beforeBegin;
    328             p->Handle.s = pasting.Handle.s + startOffsetFromStart;
     369            p->Handle.s = resultSesStart + startOffsetFromStart;
    329370        } else {
    330             assert ( p->Handle.s < afterBegin );
     371            verify ( p->Handle.s < afterBegin );
    331372            // p starts during the edit
    332             assert( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
     373            verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
    333374            if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    334375                // p ends during the edit; p does not include the last character replaced
     
    344385            }
    345386        }
    346         MoveThisAfter( p->Handle, pasting.Handle );     // move substring handle to maintain sorted order by string position
    347     }
    348 }
    349 
    350 void ?=?(string_res &s, const char* other) {
    351     assign(s, other, strlen(other));
    352 }
    353 
    354 void ?=?(string_res &s, char other) {
    355     assign(s, &other, 1);
     387        if (resultPadPosition)
     388            MoveThisAfter( p->Handle, *resultPadPosition );     // move substring handle to maintain sorted order by string position
     389    }
     390}
     391
     392static string_res & assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) {
     393
     394    // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
     395    string_res * shareEditSetStartPeer = & this;
     396    string_res * shareEditSetEndPeer = & this;
     397    for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
     398        if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
     399            shareEditSetStartPeer = editPeer;
     400        }
     401        if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
     402            shareEditSetEndPeer = editPeer;
     403        }
     404    }
     405
     406    verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
     407    size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
     408    verify( origEditSetLength >= this.Handle.lnth );
     409
     410    if ( this.shareEditSet_owns_ulink ) {                 // assigning to private context
     411        // ok to overwrite old value within LHS
     412        char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
     413        int prefixLen = this.Handle.s - prefixStartOrig;
     414        char * suffixStartOrig = this.Handle.s + this.Handle.lnth;
     415        int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
     416
     417        int delta = bsize - this.Handle.lnth;
     418        if ( char * oldBytes = VbyteTryAdjustLast( *this.Handle.ulink, delta ) ) {
     419            // growing: copy from old to new
     420            char * dest = VbyteAlloc( *this.Handle.ulink, origEditSetLength + delta );
     421            char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
     422            destCursor += prefixLen;  memcpy(destCursor, buffer         , bsize    );
     423            destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
     424            assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     425                dest,
     426                origEditSetLength + delta,
     427                0p, bsize);
     428            free( oldBytes );
     429        } else {
     430            // room is already allocated in-place: bubble suffix and overwite middle
     431            memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
     432            memcpy( this.Handle.s, buffer, bsize );
     433
     434            assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     435                shareEditSetStartPeer->Handle.s,
     436                origEditSetLength + delta,
     437                0p, bsize);
     438        }
     439
     440    } else if (                                           // assigning to shared context
     441        this.Handle.lnth == origEditSetLength &&          // overwriting entire run of SES
     442        & valSrc &&                                       // sourcing from a managed string
     443        valSrc.Handle.ulink == this.Handle.ulink  ) {     // sourcing from same heap
     444
     445        // SES's result will only use characters from the source string => reuse source
     446        assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     447            valSrc.Handle.s,
     448            valSrc.Handle.lnth,
     449            &((string_res&)valSrc).Handle, bsize);
     450       
     451    } else {
     452        // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
     453        // OR we are importing characters: need to copy eagerly (can't refer to source)
     454
     455        // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
     456        // `this` occurs in the middle of it, to be replaced
     457        // build up the new text in `pasting`
     458
     459        string_res pasting = {
     460            * this.Handle.ulink,                               // maintain same heap, regardless of context
     461            shareEditSetStartPeer->Handle.s,                   // start of SES
     462            this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
     463        append( pasting,
     464            buffer,                                            // start of replacement for this
     465            bsize );                                           // length of replacement for this
     466        append( pasting,
     467            this.Handle.s + this.Handle.lnth,                  // start of SES after this
     468            shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
     469            (this.Handle.s + this.Handle.lnth) );              // length of SES, after this
     470
     471        // The above string building can trigger compaction.
     472        // The reference points (that are arguments of the string building) may move during that building.
     473        // From this point on, they are stable.
     474
     475        assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     476            pasting.Handle.s,
     477            pasting.Handle.lnth,
     478            &pasting.Handle, bsize);
     479    }
     480
     481    return this;
     482}
     483
     484string_res & assign(string_res &this, const char* buffer, size_t bsize) {
     485    return assign_(this, buffer, bsize, *0p);
     486}
     487
     488string_res & ?=?(string_res &s, char other) {
     489    return assign(s, &other, 1);
    356490}
    357491
    358492// Copy assignment operator
    359 void ?=?(string_res & this, const string_res & rhs) with( this ) {
    360     assign(this, rhs.Handle.s, rhs.Handle.lnth);
    361 }
    362 
    363 void ?=?(string_res & this, string_res & rhs) with( this ) {
     493string_res & ?=?(string_res & this, const string_res & rhs) with( this ) {
     494    return assign_(this, rhs.Handle.s, rhs.Handle.lnth, rhs);
     495}
     496
     497string_res & ?=?(string_res & this, string_res & rhs) with( this ) {
    364498    const string_res & rhs2 = rhs;
    365     this = rhs2;
     499    return this = rhs2;
    366500}
    367501
     
    374508    s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
    375509    s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
    376     s.shareEditSet_next = &s;
    377     s.shareEditSet_prev = &s;
     510    // s.shareEditSet_next = &s;
     511    // s.shareEditSet_prev = &s;
     512
     513    if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
     514        delete( s.Handle.ulink );
     515    }
    378516}
    379517
     
    387525}
    388526
     527void assignAt(const string_res &s, size_t index, char val) {
     528    string_res editZone = { s, SHARE_EDITS, index, index+1 };
     529    assign(editZone, &val, 1);
     530}
     531
    389532
    390533///////////////////////////////////////////////////////////////////
     
    392535
    393536void append(string_res &str1, const char * buffer, size_t bsize) {
    394     size_t clnth = size(str1) + bsize;
    395     if ( str1.Handle.s + size(str1) == buffer ) { // already juxtapose ?
     537    size_t clnth = str1.Handle.lnth + bsize;
     538    if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
    396539        // no-op
    397540    } else {                                            // must copy some text
    398         if ( str1.Handle.s + size(str1) == VbyteAlloc(HeapArea, 0) ) { // str1 at end of string area ?
    399             VbyteAlloc(HeapArea, bsize); // create room for 2nd part at the end of string area
     541        if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
     542            VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
    400543        } else {                                        // copy the two parts
    401             char * str1oldBuf = str1.Handle.s;
    402             str1.Handle.s = VbyteAlloc( HeapArea, clnth );
    403             ByteCopy( HeapArea, str1.Handle.s, 0, str1.Handle.lnth, str1oldBuf, 0, str1.Handle.lnth);
     544            char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
     545            char * str1oldBuf = str1.Handle.s;  // must read after VbyteAlloc call in case it gs's
     546            str1.Handle.s = str1newBuf;
     547            memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
    404548        } // if
    405         ByteCopy( HeapArea, str1.Handle.s, str1.Handle.lnth, bsize, (char*)buffer, 0, (int)bsize);
    406         //       VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth
     549        memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
    407550    } // if
    408551    str1.Handle.lnth = clnth;
     
    417560}
    418561
    419 void ?+=?(string_res &s, const char* other) {
    420     append( s, other, strlen(other) );
    421 }
    422562
    423563
     
    429569
    430570bool ?==?(const string_res &s1, const string_res &s2) {
    431     return ByteCmp( HeapArea, s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
     571    return ByteCmp( s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
    432572}
    433573
     
    596736// Add a new HandleNode node n after the current HandleNode node.
    597737
    598 static inline void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
     738static void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
    599739#ifdef VbyteDebug
    600740    serr | "enter:AddThisAfter, this:" | &this | " n:" | &n;
    601741#endif // VbyteDebug
     742    verify( n.ulink != 0p );
     743    verify( this.ulink == n.ulink );
    602744    flink = n.flink;
    603745    blink = &n;
     
    624766// Delete the current HandleNode node.
    625767
    626 static inline void DeleteNode( HandleNode & this ) with(this) {
     768static void DeleteNode( HandleNode & this ) with(this) {
    627769#ifdef VbyteDebug
    628770    serr | "enter:DeleteNode, this:" | &this;
     
    638780
    639781// Allocates specified storage for a string from byte-string area. If not enough space remains to perform the
    640 // allocation, the garbage collection routine is called and a second attempt is made to allocate the space. If the
    641 // second attempt fails, a further attempt is made to create a new, larger byte-string area.
    642 
    643 static inline char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
     782// allocation, the garbage collection routine is called.
     783
     784static char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
    644785#ifdef VbyteDebug
    645786    serr | "enter:VbyteAlloc, size:" | size;
     
    650791    NoBytes = ( uintptr_t )EndVbyte + size;
    651792    if ( NoBytes > ( uintptr_t )ExtVbyte ) {            // enough room for new byte-string ?
    652                 garbage( this );                                        // firer up the garbage collector
    653                 NoBytes = ( uintptr_t )EndVbyte + size;         // try again
    654                 if ( NoBytes > ( uintptr_t )ExtVbyte ) {        // enough room for new byte-string ?
    655 assert( 0 && "need to implement actual growth" );
    656                         // extend( size );                              // extend the byte-string area
    657                 } // if
     793                garbage( this, size );                                  // firer up the garbage collector
     794                verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
    658795    } // if
    659796    r = EndVbyte;
     
    666803
    667804
     805// Adjusts the last allocation in this heap by delta bytes, or resets this heap to be able to offer
     806// new allocations of its original size + delta bytes. Positive delta means bigger;
     807// negative means smaller.  A null return indicates that the original heap location has room for
     808// the requested growth.  A non-null return indicates that copying to a new location is required
     809// but has not been done; the returned value is the old heap storage location; `this` heap is
     810// modified to reference the new location.  In the copy-requred case, the caller should use
     811// VbyteAlloc to claim the new space, while doing optimal copying from old to new, then free old.
     812
     813static char * VbyteTryAdjustLast( VbyteHeap & this, int delta ) with(this) {
     814
     815    if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
     816        // room available
     817        EndVbyte += delta;
     818        return 0p;
     819    }
     820
     821    char *oldBytes = StartVbyte;
     822
     823    NoOfExtensions += 1;
     824    CurrSize *= 2;
     825    StartVbyte = EndVbyte = alloc(CurrSize);
     826    ExtVbyte = StartVbyte + CurrSize;
     827
     828    return oldBytes;
     829}
     830
     831
    668832// Move an existing HandleNode node h somewhere after the current HandleNode node so that it is in ascending order by
    669833// the address in the byte string area.
    670834
    671 static inline void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
     835static void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
    672836#ifdef VbyteDebug
    673837    serr | "enter:MoveThisAfter, this:" | & this | " h:" | & h;
    674838#endif // VbyteDebug
     839    verify( h.ulink != 0p );
     840    verify( this.ulink == h.ulink );
    675841    if ( s < h.s ) {                                    // check argument values
    676842                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    677843                //      | ( h->s ) | " and keep handles in ascending order";
    678844                // exit(-1 );
    679                 assert( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
     845                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    680846    } // if
    681847
     
    709875//######################### VbyteHeap #########################
    710876
    711 // Move characters from one location in the byte-string area to another. The routine handles the following situations:
    712 //
    713 // if the |Src| > |Dst| => truncate
    714 // if the |Dst| > |Src| => pad Dst with blanks
    715 
    716 void ByteCopy( VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth ) {
    717     for ( int i = 0; i < DstLnth; i += 1 ) {
    718       if ( i == SrcLnth ) {                             // |Dst| > |Src|
    719             for ( ; i < DstLnth; i += 1 ) {             // pad Dst with blanks
    720                 Dst[DstStart + i] = ' ';
    721             } // for
    722             break;
    723         } // exit
    724         Dst[DstStart + i] = Src[SrcStart + i];
    725     } // for
    726 } // ByteCopy
    727 
    728877// Compare two byte strings in the byte-string area. The routine returns the following values:
    729878//
     
    732881// -1 => Src1-byte-string < Src2-byte-string
    733882
    734 int ByteCmp( VbyteHeap & this, char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  with(this) {
     883int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) {
    735884#ifdef VbyteDebug
    736885    serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
     
    789938    h = Header.flink;                                   // ignore header node
    790939    for (;;) {
    791                 ByteCopy( this, EndVbyte, 0, h->lnth, h->s, 0, h->lnth );
     940                memmove( EndVbyte, h->s, h->lnth );
    792941                obase = h->s;
    793942                h->s = EndVbyte;
     
    813962// the heap.  The heap is then compacted in the existing heap or into the newly allocated heap.
    814963
    815 void garbage(VbyteHeap & this ) with(this) {
     964void garbage(VbyteHeap & this, int minreq ) with(this) {
    816965#ifdef VbyteDebug
    817966    serr | "enter:garbage";
     
    837986    AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
    838987   
    839     if ( AmountFree < ( int )( CurrSize * 0.1 )) {      // free space less than 10% ?
    840 
    841 assert( 0 && "need to implement actual growth" );
    842 //              extend( CurrSize );                             // extend the heap
     988    if ( ( double ) AmountFree < ( CurrSize * 0.1 ) || AmountFree < minreq ) {  // free space less than 10%  or not enough to serve cur request
     989
     990                extend( this, max( CurrSize, minreq ) );                                // extend the heap
    843991
    844992                        //  Peter says, "This needs work before it should be used."
     
    846994                        //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
    847995
    848     } // if
    849     compaction(this);                                   // compact the byte area, in the same or new heap area
     996        // `extend` implies a `compaction` during the copy
     997
     998    } else {
     999        compaction(this);                                       // in-place
     1000    }// if
    8501001#ifdef VbyteDebug
    8511002    {
     
    8671018#undef VbyteDebug
    8681019
    869 //WIP
    870 #if 0
    8711020
    8721021
     
    8741023// area is deleted.
    8751024
    876 void VbyteHeap::extend( int size ) {
     1025void extend( VbyteHeap & this, int size ) with (this) {
    8771026#ifdef VbyteDebug
    8781027    serr | "enter:extend, size:" | size;
     
    8841033   
    8851034    CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
    886     StartVbyte = EndVbyte = new char[CurrSize];
     1035    StartVbyte = EndVbyte = alloc(CurrSize);
    8871036    ExtVbyte = (void *)( StartVbyte + CurrSize );
    888     compaction();                                       // copy from old heap to new & adjust pointers to new heap
    889     delete OldStartVbyte;                               // release old heap
     1037    compaction(this);                                   // copy from old heap to new & adjust pointers to new heap
     1038    free( OldStartVbyte );                              // release old heap
    8901039#ifdef VbyteDebug
    8911040    serr | "exit:extend, CurrSize:" | CurrSize;
     
    8931042} // extend
    8941043
     1044//WIP
     1045#if 0
    8951046
    8961047// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
Note: See TracChangeset for help on using the changeset viewer.