Ignore:
Timestamp:
Mar 21, 2022, 1:44:06 PM (4 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, ast-experimental, enum, master, pthread-emulation, qualifiedEnum
Children:
a76202d
Parents:
ef3c383 (diff), dbe2533 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

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

    ref3c383 rd672350  
    1515
    1616#include "string_res.hfa"
    17 #include <stdlib.hfa>  // e.g. malloc
    18 #include <string.h>    // e.g. strlen
     17#include "string_sharectx.hfa"
     18#include "stdlib.hfa"
     19
     20// Workaround for observed performance penalty from calling CFA's alloc.
     21// Workaround is:  EndVbyte = TEMP_ALLOC(char, CurrSize)
     22// Should be:      EndVbyte = alloc(CurrSize)
     23#define TEMP_ALLOC(T, n) (( T* ) malloc( n * sizeof( T ) ))
     24
     25#include <assert.h>
    1926
    2027//######################### VbyteHeap "header" #########################
    21 
    22 
    23 
    24 
    25 
    26 
    27 
    28 
    29 // DON'T COMMIT:
    30 // #define VbyteDebug
    31 
    32 
    33 
    34 
    3528
    3629#ifdef VbyteDebug
     
    5447
    5548   
    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
     49static void compaction( VbyteHeap & );                          // compaction of the byte area
     50static void garbage( VbyteHeap &, int );                                // garbage collect the byte area
     51static void extend( VbyteHeap &, int );                 // extend the size of the byte area
     52static void reduce( VbyteHeap &, int );                 // reduce the size of the byte area
     53
     54static void ?{}( VbyteHeap &, size_t = 1000 );
     55static void ^?{}( VbyteHeap & );
     56
     57static int ByteCmp( char *, int, int, char *, int, int );       // compare 2 blocks of bytes
     58static char *VbyteAlloc( VbyteHeap &, int );                    // allocate a block bytes in the heap
     59static char *VbyteTryAdjustLast( VbyteHeap &, int );
     60
     61static void AddThisAfter( HandleNode &, HandleNode & );
     62static void DeleteNode( HandleNode & );
     63static void MoveThisAfter( HandleNode &, const HandleNode & );          // move current handle after parameter handle
    7164
    7265
    7366// Allocate the storage for the variable sized area and intialize the heap variables.
    7467
    75 static inline void ?{}( VbyteHeap & this, int Size ) with(this) {
     68static void ?{}( VbyteHeap & this, size_t Size ) with(this) {
    7669#ifdef VbyteDebug
    7770    serr | "enter:VbyteHeap::VbyteHeap, this:" | &this | " Size:" | Size;
     
    7972    NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
    8073    InitSize = CurrSize = Size;
    81     StartVbyte = EndVbyte = alloc(CurrSize);
     74    StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    8275    ExtVbyte = (void *)( StartVbyte + CurrSize );
    8376    Header.flink = Header.blink = &Header;
     77    Header.ulink = & this;
    8478#ifdef VbyteDebug
    8579    HeaderPtr = &Header;
     
    9185// Release the dynamically allocated storage for the byte area.
    9286
    93 static inline void ^?{}( VbyteHeap & this ) with(this) {
     87static void ^?{}( VbyteHeap & this ) with(this) {
    9488    free( StartVbyte );
    9589} // ~VbyteHeap
     
    10296// creator.
    10397
    104 void ?{}( HandleNode & this ) with(this) {
     98static void ?{}( HandleNode & this ) with(this) {
    10599#ifdef VbyteDebug
    106100    serr | "enter:HandleNode::HandleNode, this:" | &this;
     
    117111// collection.
    118112
    119 void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
     113static void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
    120114#ifdef VbyteDebug
    121115    serr | "enter:HandleNode::HandleNode, this:" | &this;
     
    123117    s = 0;
    124118    lnth = 0;
     119    ulink = &vh;
    125120    AddThisAfter( this, *vh.Header.blink );
    126121#ifdef VbyteDebug
     
    133128// is the responsibility of the creator to destroy it.
    134129
    135 void ^?{}( HandleNode & this ) with(this) {
     130static void ^?{}( HandleNode & this ) with(this) {
    136131#ifdef VbyteDebug
    137132    serr | "enter:HandleNode::~HandleNode, this:" | & this;
     
    149144} // ~HandleNode
    150145
     146
     147//######################### String Sharing Context #########################
     148
     149static string_sharectx * ambient_string_sharectx;               // fickle top of stack
     150static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack
     151
     152void ?{}( string_sharectx & this, StringSharectx_Mode mode ) with( this ) {
     153    (older){ ambient_string_sharectx };
     154    if ( mode == NEW_SHARING ) {
     155        (activeHeap){ new( (size_t) 1000 ) };
     156    } else {
     157        verify( mode == NO_SHARING );
     158        (activeHeap){ 0p };
     159    }
     160    ambient_string_sharectx = & this;
     161}
     162
     163void ^?{}( string_sharectx & this ) with( this ) {
     164    if ( activeHeap ) delete( activeHeap );
     165
     166    // unlink this from older-list starting from ambient_string_sharectx
     167    // usually, this==ambient_string_sharectx and the loop runs zero times
     168    string_sharectx *& c = ambient_string_sharectx;
     169    while ( c != &this ) &c = &c->older;              // find this
     170    c = this.older;                                   // unlink
     171}
     172
    151173//######################### String Resource #########################
    152174
    153175
    154 VbyteHeap HeapArea;
    155 
    156 VbyteHeap * DEBUG_string_heap = & HeapArea;
     176VbyteHeap * DEBUG_string_heap() {
     177    assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
     178    return ambient_string_sharectx->activeHeap;
     179}
    157180
    158181size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) {
     
    160183}
    161184
     185size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) {
     186    return heap->CurrSize;
     187}
     188
    162189const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
    163190    return heap->StartVbyte;
    164191}
    165 
    166192
    167193// Returns the size of the string in bytes
     
    187213    // Store auto-newline state so it can be restored
    188214    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;
     215    if( s.Handle.lnth == 0 ) {
     216        sout | "";
     217    } else {
     218        nlOff(out);
     219        for (size_t i = 0; i < s.Handle.lnth; i++) {
     220            // Need to re-apply on the last output operator, for whole-statement version
     221            if (anl && i == s.Handle.lnth-1) nlOn(out);
     222            out | s[i];
     223        }
     224    }
    196225}
    197226
    198227// Empty constructor
    199228void ?{}(string_res &s) with(s) {
    200     (Handle){ HeapArea };
     229    if( ambient_string_sharectx->activeHeap ) {
     230        (Handle){ * ambient_string_sharectx->activeHeap };
     231        (shareEditSet_owns_ulink){ false };
     232        verify( Handle.s == 0p && Handle.lnth == 0 );
     233    } else {
     234        (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
     235        (shareEditSet_owns_ulink){ true };
     236        Handle.s = Handle.ulink->StartVbyte;
     237        verify( Handle.lnth == 0 );
     238    }
    201239    s.shareEditSet_prev = &s;
    202240    s.shareEditSet_next = &s;
    203241}
    204242
     243static void eagerCopyCtorHelper(string_res &s, const char* rhs, size_t rhslnth) with(s) {
     244    if( ambient_string_sharectx->activeHeap ) {
     245        (Handle){ * ambient_string_sharectx->activeHeap };
     246        (shareEditSet_owns_ulink){ false };
     247    } else {
     248        (Handle){ * new( rhslnth ) };
     249        (shareEditSet_owns_ulink){ true };
     250    }
     251    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     252    Handle.lnth = rhslnth;
     253    memmove( Handle.s, rhs, rhslnth );
     254    s.shareEditSet_prev = &s;
     255    s.shareEditSet_next = &s;
     256}
     257
    205258// Constructor from a raw buffer and size
    206259void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
    207     (Handle){ HeapArea };
    208     Handle.s = VbyteAlloc(HeapArea, rhslnth);
     260    eagerCopyCtorHelper(s, rhs, rhslnth);
     261}
     262
     263// private ctor (not in header): use specified heap (ignore ambient) and copy chars in
     264void ?{}( string_res &s, VbyteHeap & heap, const char* rhs, size_t rhslnth ) with(s) {
     265    (Handle){ heap };
     266    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    209267    Handle.lnth = rhslnth;
    210     for ( int i = 0; i < rhslnth; i += 1 ) {            // copy characters
    211         Handle.s[i] = rhs[i];
    212     } // for
     268    (s.shareEditSet_owns_ulink){ false };
     269    memmove( Handle.s, rhs, rhslnth );
    213270    s.shareEditSet_prev = &s;
    214271    s.shareEditSet_next = &s;
    215272}
    216273
    217 // String literal constructor
    218 void ?{}(string_res &s, const char* rhs) {
    219     (s){ rhs, strlen(rhs) };
    220 }
    221 
    222274// General copy constructor
    223275void ?{}(string_res &s, const string_res & s2, StrResInitMode mode, size_t start, size_t end ) {
    224276
    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;
     277    verify( start <= end && end <= s2.Handle.lnth );
     278
     279    if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
     280        // crossing heaps (including private): copy eagerly
     281        eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
     282        verify(s.shareEditSet_prev == &s);
     283        verify(s.shareEditSet_next == &s);
    235284    } 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;
     285        (s.Handle){};
     286        s.Handle.s = s2.Handle.s + start;
     287        s.Handle.lnth = end - start;
     288        s.Handle.ulink = s2.Handle.ulink;
     289
     290        AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
     291        // ^ bug?  skip others at early point in string
     292
     293        if (mode == COPY_VALUE) {
     294            verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
     295            // requested logical copy in same heap: defer copy until write
     296
     297            (s.shareEditSet_owns_ulink){ false };
     298
     299            // make s alone in its shareEditSet
     300            s.shareEditSet_prev = &s;
     301            s.shareEditSet_next = &s;
     302        } else {
     303            verify( mode == SHARE_EDITS );
     304            // sharing edits with source forces same heap as source (ignore context)
     305
     306            (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
     307
     308            // s2 is logically const but not implementation const
     309            string_res & s2mod = (string_res &) s2;
     310
     311            // insert s after s2 on shareEditSet
     312            s.shareEditSet_next = s2mod.shareEditSet_next;
     313            s.shareEditSet_prev = &s2mod;
     314            s.shareEditSet_next->shareEditSet_prev = &s;
     315            s.shareEditSet_prev->shareEditSet_next = &s;
    257316        }
    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.
     317    }
     318}
     319
     320static void assignEditSet(string_res & this, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
     321    char * resultSesStart,
     322    size_t resultSesLnth,
     323    HandleNode * resultPadPosition, size_t bsize ) {
    283324
    284325    char * beforeBegin = shareEditSetStartPeer->Handle.s;
     
    290331    size_t oldLnth = this.Handle.lnth;
    291332
    292     this.Handle.s = pasting.Handle.s + beforeLen;
     333    this.Handle.s = resultSesStart + beforeLen;
    293334    this.Handle.lnth = bsize;
    294     MoveThisAfter( this.Handle, pasting.Handle );
     335    if (resultPadPosition)
     336        MoveThisAfter( this.Handle, *resultPadPosition );
    295337
    296338    // 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;
     339    char *limit = resultSesStart + resultSesLnth;
    298340    for (string_res * p = this.shareEditSet_next; p != &this; p = p->shareEditSet_next) {
    299         assert (p->Handle.s >= beforeBegin);
     341        verify (p->Handle.s >= beforeBegin);
    300342        if ( p->Handle.s >= afterBegin ) {
    301             assert ( p->Handle.s <= afterBegin + afterLen );
    302             assert ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     343            verify ( p->Handle.s <= afterBegin + afterLen );
     344            verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    303345            // p starts after the edit
    304346            // take start and end as end-anchored
     
    318360            } else {
    319361                // p ends after the edit
    320                 assert ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     362                verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    321363                // take end as end-anchored
    322364                // stretch-shrink p according to the edit
     
    326368            // take start as start-anchored
    327369            size_t startOffsetFromStart = p->Handle.s - beforeBegin;
    328             p->Handle.s = pasting.Handle.s + startOffsetFromStart;
     370            p->Handle.s = resultSesStart + startOffsetFromStart;
    329371        } else {
    330             assert ( p->Handle.s < afterBegin );
     372            verify ( p->Handle.s < afterBegin );
    331373            // p starts during the edit
    332             assert( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
     374            verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
    333375            if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    334376                // p ends during the edit; p does not include the last character replaced
     
    344386            }
    345387        }
    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);
     388        if (resultPadPosition)
     389            MoveThisAfter( p->Handle, *resultPadPosition );     // move substring handle to maintain sorted order by string position
     390    }
     391}
     392
     393static string_res & assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) {
     394
     395    // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
     396    string_res * shareEditSetStartPeer = & this;
     397    string_res * shareEditSetEndPeer = & this;
     398    for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
     399        if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
     400            shareEditSetStartPeer = editPeer;
     401        }
     402        if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
     403            shareEditSetEndPeer = editPeer;
     404        }
     405    }
     406
     407    verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
     408    size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
     409    verify( origEditSetLength >= this.Handle.lnth );
     410
     411    if ( this.shareEditSet_owns_ulink ) {                 // assigning to private context
     412        // ok to overwrite old value within LHS
     413        char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
     414        int prefixLen = this.Handle.s - prefixStartOrig;
     415        char * suffixStartOrig = this.Handle.s + this.Handle.lnth;
     416        int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
     417
     418        int delta = bsize - this.Handle.lnth;
     419        if ( char * oldBytes = VbyteTryAdjustLast( *this.Handle.ulink, delta ) ) {
     420            // growing: copy from old to new
     421            char * dest = VbyteAlloc( *this.Handle.ulink, origEditSetLength + delta );
     422            char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
     423            destCursor += prefixLen;  memcpy(destCursor, buffer         , bsize    );
     424            destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
     425            assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     426                dest,
     427                origEditSetLength + delta,
     428                0p, bsize);
     429            free( oldBytes );
     430        } else {
     431            // room is already allocated in-place: bubble suffix and overwite middle
     432            memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
     433            memcpy( this.Handle.s, buffer, bsize );
     434
     435            assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     436                shareEditSetStartPeer->Handle.s,
     437                origEditSetLength + delta,
     438                0p, bsize);
     439        }
     440
     441    } else if (                                           // assigning to shared context
     442        this.Handle.lnth == origEditSetLength &&          // overwriting entire run of SES
     443        & valSrc &&                                       // sourcing from a managed string
     444        valSrc.Handle.ulink == this.Handle.ulink  ) {     // sourcing from same heap
     445
     446        // SES's result will only use characters from the source string => reuse source
     447        assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     448            valSrc.Handle.s,
     449            valSrc.Handle.lnth,
     450            &((string_res&)valSrc).Handle, bsize);
     451       
     452    } else {
     453        // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
     454        // OR we are importing characters: need to copy eagerly (can't refer to source)
     455
     456        // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
     457        // `this` occurs in the middle of it, to be replaced
     458        // build up the new text in `pasting`
     459
     460        string_res pasting = {
     461            * this.Handle.ulink,                               // maintain same heap, regardless of context
     462            shareEditSetStartPeer->Handle.s,                   // start of SES
     463            this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
     464        append( pasting,
     465            buffer,                                            // start of replacement for this
     466            bsize );                                           // length of replacement for this
     467        append( pasting,
     468            this.Handle.s + this.Handle.lnth,                  // start of SES after this
     469            shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
     470            (this.Handle.s + this.Handle.lnth) );              // length of SES, after this
     471
     472        // The above string building can trigger compaction.
     473        // The reference points (that are arguments of the string building) may move during that building.
     474        // From this point on, they are stable.
     475
     476        assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
     477            pasting.Handle.s,
     478            pasting.Handle.lnth,
     479            &pasting.Handle, bsize);
     480    }
     481
     482    return this;
     483}
     484
     485string_res & assign(string_res &this, const char* buffer, size_t bsize) {
     486    return assign_(this, buffer, bsize, *0p);
     487}
     488
     489string_res & ?=?(string_res &s, char other) {
     490    return assign(s, &other, 1);
    356491}
    357492
    358493// 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 ) {
     494string_res & ?=?(string_res & this, const string_res & rhs) with( this ) {
     495    return assign_(this, rhs.Handle.s, rhs.Handle.lnth, rhs);
     496}
     497
     498string_res & ?=?(string_res & this, string_res & rhs) with( this ) {
    364499    const string_res & rhs2 = rhs;
    365     this = rhs2;
     500    return this = rhs2;
    366501}
    367502
     
    374509    s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
    375510    s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
    376     s.shareEditSet_next = &s;
    377     s.shareEditSet_prev = &s;
     511    // s.shareEditSet_next = &s;
     512    // s.shareEditSet_prev = &s;
     513
     514    if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
     515        delete( s.Handle.ulink );
     516    }
    378517}
    379518
     
    387526}
    388527
     528void assignAt(const string_res &s, size_t index, char val) {
     529    string_res editZone = { s, SHARE_EDITS, index, index+1 };
     530    assign(editZone, &val, 1);
     531}
     532
    389533
    390534///////////////////////////////////////////////////////////////////
     
    392536
    393537void 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 ?
     538    size_t clnth = str1.Handle.lnth + bsize;
     539    if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
    396540        // no-op
    397541    } 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
     542        if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
     543            VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
    400544        } 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);
     545            char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
     546            char * str1oldBuf = str1.Handle.s;  // must read after VbyteAlloc call in case it gs's
     547            str1.Handle.s = str1newBuf;
     548            memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
    404549        } // 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
     550        memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
    407551    } // if
    408552    str1.Handle.lnth = clnth;
     
    417561}
    418562
    419 void ?+=?(string_res &s, const char* other) {
    420     append( s, other, strlen(other) );
    421 }
    422563
    423564
     
    429570
    430571bool ?==?(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;
     572    return ByteCmp( s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
    432573}
    433574
     
    455596
    456597int find(const string_res &s, char search) {
    457     for (i; size(s)) {
    458         if (s[i] == search) return i;
    459     }
    460     return size(s);
    461 }
     598    return findFrom(s, 0, search);
     599}
     600
     601int findFrom(const string_res &s, size_t fromPos, char search) {
     602    // FIXME: This paricular overload (find of single char) is optimized to use memchr.
     603    // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
     604    char * searchFrom = s.Handle.s + fromPos;
     605    size_t searchLnth = s.Handle.lnth - fromPos;
     606    int searchVal = search;
     607    char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
     608    if (foundAt == 0p) return s.Handle.lnth;
     609    else return foundAt - s.Handle.s;
     610}
     611
     612int find(const string_res &s, const string_res &search) {
     613    return findFrom(s, 0, search);
     614}
     615
     616int findFrom(const string_res &s, size_t fromPos, const string_res &search) {
     617    return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
     618}
     619
     620int find(const string_res &s, const char* search) {
     621    return findFrom(s, 0, search);
     622}
     623int findFrom(const string_res &s, size_t fromPos, const char* search) {
     624    return findFrom(s, fromPos, search, strlen(search));
     625}
     626
     627int find(const string_res &s, const char* search, size_t searchsize) {
     628    return findFrom(s, 0, search, searchsize);
     629}
     630
     631int findFrom(const string_res &s, size_t fromPos, const char* search, size_t searchsize) {
    462632
    463633    /* Remaining implementations essentially ported from Sunjay's work */
    464634
    465 int find(const string_res &s, const string_res &search) {
    466     return find(s, search.Handle.s, search.Handle.lnth);
    467 }
    468 
    469 int find(const string_res &s, const char* search) {
    470     return find(s, search, strlen(search));
    471 }
    472 
    473 int find(const string_res &s, const char* search, size_t searchsize) {
     635
    474636    // FIXME: This is a naive algorithm. We probably want to switch to someting
    475637    // like Boyer-Moore in the future.
     
    481643    }
    482644
    483     for (size_t i = 0; i < s.Handle.lnth; i++) {
     645    for (size_t i = fromPos; i < s.Handle.lnth; i++) {
    484646        size_t remaining = s.Handle.lnth - i;
    485647        // Never going to find the search string if the remaining string is
     
    596758// Add a new HandleNode node n after the current HandleNode node.
    597759
    598 static inline void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
     760static void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
    599761#ifdef VbyteDebug
    600762    serr | "enter:AddThisAfter, this:" | &this | " n:" | &n;
    601763#endif // VbyteDebug
     764    // Performance note: we are on the critical path here. MB has ensured that the verifies don't contribute to runtime (are compiled away, like they're supposed to be).
     765    verify( n.ulink != 0p );
     766    verify( this.ulink == n.ulink );
    602767    flink = n.flink;
    603768    blink = &n;
     
    624789// Delete the current HandleNode node.
    625790
    626 static inline void DeleteNode( HandleNode & this ) with(this) {
     791static void DeleteNode( HandleNode & this ) with(this) {
    627792#ifdef VbyteDebug
    628793    serr | "enter:DeleteNode, this:" | &this;
     
    638803
    639804// 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) {
     805// allocation, the garbage collection routine is called.
     806
     807static char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
    644808#ifdef VbyteDebug
    645809    serr | "enter:VbyteAlloc, size:" | size;
     
    650814    NoBytes = ( uintptr_t )EndVbyte + size;
    651815    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
     816                garbage( this, size );                                  // firer up the garbage collector
     817                verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
    658818    } // if
    659819    r = EndVbyte;
     
    666826
    667827
     828// Adjusts the last allocation in this heap by delta bytes, or resets this heap to be able to offer
     829// new allocations of its original size + delta bytes. Positive delta means bigger;
     830// negative means smaller.  A null return indicates that the original heap location has room for
     831// the requested growth.  A non-null return indicates that copying to a new location is required
     832// but has not been done; the returned value is the old heap storage location; `this` heap is
     833// modified to reference the new location.  In the copy-requred case, the caller should use
     834// VbyteAlloc to claim the new space, while doing optimal copying from old to new, then free old.
     835
     836static char * VbyteTryAdjustLast( VbyteHeap & this, int delta ) with(this) {
     837
     838    if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
     839        // room available
     840        EndVbyte += delta;
     841        return 0p;
     842    }
     843
     844    char *oldBytes = StartVbyte;
     845
     846    NoOfExtensions += 1;
     847    CurrSize *= 2;
     848    StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     849    ExtVbyte = StartVbyte + CurrSize;
     850
     851    return oldBytes;
     852}
     853
     854
    668855// Move an existing HandleNode node h somewhere after the current HandleNode node so that it is in ascending order by
    669856// the address in the byte string area.
    670857
    671 static inline void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
     858static void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
    672859#ifdef VbyteDebug
    673860    serr | "enter:MoveThisAfter, this:" | & this | " h:" | & h;
    674861#endif // VbyteDebug
     862    verify( h.ulink != 0p );
     863    verify( this.ulink == h.ulink );
    675864    if ( s < h.s ) {                                    // check argument values
    676865                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    677866                //      | ( h->s ) | " and keep handles in ascending order";
    678867                // exit(-1 );
    679                 assert( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
     868                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    680869    } // if
    681870
     
    709898//######################### VbyteHeap #########################
    710899
    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 
    728900// Compare two byte strings in the byte-string area. The routine returns the following values:
    729901//
     
    732904// -1 => Src1-byte-string < Src2-byte-string
    733905
    734 int ByteCmp( VbyteHeap & this, char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  with(this) {
     906int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) {
    735907#ifdef VbyteDebug
    736908    serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
     
    789961    h = Header.flink;                                   // ignore header node
    790962    for (;;) {
    791                 ByteCopy( this, EndVbyte, 0, h->lnth, h->s, 0, h->lnth );
     963                memmove( EndVbyte, h->s, h->lnth );
    792964                obase = h->s;
    793965                h->s = EndVbyte;
     
    810982
    811983
     984static double heap_expansion_freespace_threshold = 0.1;  // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage)
     985                                                         // probably an unreasonable default, but need to assess early-round tests on changing it
     986
     987void TUNING_set_string_heap_liveness_threshold( double val ) {
     988    heap_expansion_freespace_threshold = 1.0 - val;
     989}
     990
     991
    812992// Garbage determines the amount of free space left in the heap and then reduces, leave the same, or extends the size of
    813993// the heap.  The heap is then compacted in the existing heap or into the newly allocated heap.
    814994
    815 void garbage(VbyteHeap & this ) with(this) {
     995void garbage(VbyteHeap & this, int minreq ) with(this) {
    816996#ifdef VbyteDebug
    817997    serr | "enter:garbage";
     
    8371017    AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
    8381018   
    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
     1019    if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {   // free space less than threshold or not enough to serve cur request
     1020
     1021                extend( this, max( CurrSize, minreq ) );                                // extend the heap
    8431022
    8441023                        //  Peter says, "This needs work before it should be used."
     
    8461025                        //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
    8471026
    848     } // if
    849     compaction(this);                                   // compact the byte area, in the same or new heap area
     1027        // `extend` implies a `compaction` during the copy
     1028
     1029    } else {
     1030        compaction(this);                                       // in-place
     1031    }// if
    8501032#ifdef VbyteDebug
    8511033    {
     
    8671049#undef VbyteDebug
    8681050
    869 //WIP
    870 #if 0
    8711051
    8721052
     
    8741054// area is deleted.
    8751055
    876 void VbyteHeap::extend( int size ) {
     1056void extend( VbyteHeap & this, int size ) with (this) {
    8771057#ifdef VbyteDebug
    8781058    serr | "enter:extend, size:" | size;
     
    8841064   
    8851065    CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
    886     StartVbyte = EndVbyte = new char[CurrSize];
     1066    StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    8871067    ExtVbyte = (void *)( StartVbyte + CurrSize );
    888     compaction();                                       // copy from old heap to new & adjust pointers to new heap
    889     delete OldStartVbyte;                               // release old heap
     1068    compaction(this);                                   // copy from old heap to new & adjust pointers to new heap
     1069    free( OldStartVbyte );                              // release old heap
    8901070#ifdef VbyteDebug
    8911071    serr | "exit:extend, CurrSize:" | CurrSize;
     
    8931073} // extend
    8941074
     1075//WIP
     1076#if 0
    8951077
    8961078// 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.