Changes in / [db1ebed:5235d49]


Ignore:
Files:
19 added
10 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/Makefile.am

    rdb1ebed r5235d49  
    6363        containers/queueLockFree.hfa \
    6464        containers/stackLockFree.hfa \
     65        containers/string_sharectx.hfa \
    6566        containers/vector2.hfa \
    6667        vec/vec.hfa \
  • libcfa/src/containers/string.cfa

    rdb1ebed r5235d49  
    9292}
    9393
    94 string ?=?(string & this, string other) {
     94string & ?=?(string & this, string & other) { //// <---- straw man change
    9595    (*this.inner) = (*other.inner);
    9696    return this;
  • libcfa/src/containers/string.hfa

    rdb1ebed r5235d49  
    4141void ?=?(string &s, const string &other);
    4242void ?=?(string &s, char other);
    43 string ?=?(string &s, string other);  // string tolerates memcpys; still saw calls to autogen
    44 
     43string & ?=?(string &s, string &other);  // surprising ret seems to help avoid calls to autogen
     44//string ?=?( string &, string ) = void;
    4545void ^?{}(string &s);
    4646
  • libcfa/src/containers/string_res.cfa

    rdb1ebed r5235d49  
    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";
     
    831980    int AmountUsed, AmountFree;
    832981
     982//    assert( false );
     983
    833984    AmountUsed = 0;
    834985    for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
     
    837988    AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
    838989   
    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
     990    if ( ( double ) AmountFree < ( CurrSize * 0.1 ) || AmountFree < minreq ) {  // free space less than 10%  or not enough to serve cur request
     991
     992                extend( this, max( CurrSize, minreq ) );                                // extend the heap
    843993
    844994                        //  Peter says, "This needs work before it should be used."
     
    8671017#undef VbyteDebug
    8681018
    869 //WIP
    870 #if 0
    8711019
    8721020
     
    8741022// area is deleted.
    8751023
    876 void VbyteHeap::extend( int size ) {
     1024void extend( VbyteHeap & this, int size ) with (this) {
    8771025#ifdef VbyteDebug
    8781026    serr | "enter:extend, size:" | size;
     
    8841032   
    8851033    CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
    886     StartVbyte = EndVbyte = new char[CurrSize];
     1034    StartVbyte = EndVbyte = alloc(CurrSize);
    8871035    ExtVbyte = (void *)( StartVbyte + CurrSize );
    888     compaction();                                       // copy from old heap to new & adjust pointers to new heap
    889     delete OldStartVbyte;                               // release old heap
     1036    compaction(this);                                   // copy from old heap to new & adjust pointers to new heap
     1037    free( OldStartVbyte );                              // release old heap
    8901038#ifdef VbyteDebug
    8911039    serr | "exit:extend, CurrSize:" | CurrSize;
     
    8931041} // extend
    8941042
     1043//WIP
     1044#if 0
    8951045
    8961046// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
  • libcfa/src/containers/string_res.hfa

    rdb1ebed r5235d49  
    1717
    1818#include <fstream.hfa>
     19#include <string.h>    // e.g. strlen
    1920
    2021   
     
    2728    HandleNode *flink;                                  // forward link
    2829    HandleNode *blink;                                  // backward link
     30    VbyteHeap *ulink;                   // upward link
    2931
    3032    char *s;                                            // pointer to byte string
     
    3234}; // HandleNode
    3335
    34 void ?{}( HandleNode & );                       // constructor for header node
    35 
    36 void ?{}( HandleNode &, VbyteHeap & );          // constructor for nodes in the handle list
    37 void ^?{}( HandleNode & );                      // destructor for handle nodes
    38 
    39 extern VbyteHeap * DEBUG_string_heap;
     36VbyteHeap * DEBUG_string_heap();
     37size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap );
    4038size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap );
    4139const char * DEBUG_string_heap_start( VbyteHeap * heap );
     
    4745struct string_res {
    4846    HandleNode Handle; // chars, start, end, global neighbours
     47    bool shareEditSet_owns_ulink;
    4948    string_res * shareEditSet_prev;
    5049    string_res * shareEditSet_next;
     
    7473// Constructors, Assignment Operators, Destructor
    7574void ?{}(string_res &s); // empty string
    76 void ?{}(string_res &s, const char* initial); // copy from string literal (NULL-terminated)
    7775void ?{}(string_res &s, const char* buffer, size_t bsize); // copy specific length from buffer
     76static inline void ?{}(string_res &s, const char* rhs) { // copy from string literal (NULL-terminated)
     77    (s){ rhs, strlen(rhs) };
     78}
    7879
    7980void ?{}(string_res &s, const string_res & s2) = void;
     
    8687}
    8788
    88 void assign(string_res &s, const char* buffer, size_t bsize); // copy specific length from buffer
    89 void ?=?(string_res &s, const char* other); // copy from string literal (NULL-terminated)
    90 void ?=?(string_res &s, const string_res &other);
    91 void ?=?(string_res &s, string_res &other);
    92 void ?=?(string_res &s, char other);
     89string_res & assign(string_res &s, const char* buffer, size_t bsize); // copy specific length from buffer
     90static inline string_res & ?=?(string_res &s, const char* other) {  // copy from string literal (NULL-terminated)
     91    return assign(s, other, strlen(other));
     92}
     93string_res & ?=?(string_res &s, const string_res &other);
     94string_res & ?=?(string_res &s, string_res &other);
     95string_res & ?=?(string_res &s, char other);
    9396
    9497void ^?{}(string_res &s);
     
    99102
    100103// Concatenation
     104void append(string_res &s, const char* buffer, size_t bsize);
    101105void ?+=?(string_res &s, char other); // append a character
    102106void ?+=?(string_res &s, const string_res &s2); // append-concatenate to first string
    103 void ?+=?(string_res &s, const char* other);
    104 void append(string_res &s, const char* buffer, size_t bsize);
     107static inline void ?+=?(string_res &s, const char* other) {
     108    append( s, other, strlen(other) );
     109}
    105110
    106111// Character access
     112void assignAt(const string_res &s, size_t index, char val);
    107113char ?[?](const string_res &s, size_t index); // Mike changed to ret by val from Sunjay's ref, to match Peter's
    108114//char codePointAt(const string_res &s, size_t index); // revisit under Unicode
  • tests/collections/.expect/string-api-coverage.txt

    rdb1ebed r5235d49  
    11hello hello hello
     2
     3hello
    24true false
    35true false
  • tests/collections/.expect/string-gc.txt

    rdb1ebed r5235d49  
    3838x from 5 to 15
    3939y from 5 to 15
     40======================== fillNoCompact
     41about to expand, a = aaa
     42expanded, a = aaa
     43about to expand, a = aaa
     44expanded, a = aaa
     45about to expand, a = aaa
     46expanded, a = aaa
     47about to expand, a = aaa
     48expanded, a = aaa
     49about to expand, a = aaa
     50expanded, a = aaa
  • tests/collections/string-api-coverage.cfa

    rdb1ebed r5235d49  
    11#include <containers/string.hfa>
     2#include <string_sharectx.hfa>
    23
    34void assertWellFormedHandleList( int maxLen ) { // with(HeapArea)
     
    2526
    2627int main () {
     28
     29    #ifdef STRING_SHARING_OFF
     30    string_sharectx c = { NO_SHARING };
     31    #endif
     32
    2733    string s = "hello";
    2834    string s2 = "hello";
     
    3137
    3238    // IO operator, x2
    33     sout | s | s | s;
     39    sout | s | s | s;  // hello hello hello
     40
     41    // empty ctor then assign
     42    string sxx;
     43    sout | sxx;  // (blank line)
     44    sxx = s;
     45    sout | sxx;  // hello
    3446
    3547    // Comparisons
  • tests/collections/string-gc.cfa

    rdb1ebed r5235d49  
    22
    33size_t bytesRemaining() {
    4     return DEBUG_string_bytes_avail_until_gc( DEBUG_string_heap );
     4    return DEBUG_string_bytes_avail_until_gc( DEBUG_string_heap() );
    55}
    66
    77size_t heapOffsetStart( string_res & s ) {
    8     const char * startByte = DEBUG_string_heap_start( DEBUG_string_heap );
     8    const char * startByte = DEBUG_string_heap_start( DEBUG_string_heap() );
    99    assert( s.Handle.s >= startByte );
    1010    return s.Handle.s - startByte;
     
    120120}
    121121
     122void fillNoCompact() {
     123    // show that allocating in a heap filled with mostly live strings (no collectable garbage) causes heap growth
     124
     125    sout | "======================== fillNoCompact";
     126
     127    size_t lastTimeBytesAvail = bytesRemaining();
     128    assert( lastTimeBytesAvail >= 200 ); // starting this test with nontrivial room
     129
     130    // mostly fill the pad
     131    string_res a = "aaa";  // will have to be moved
     132    string_res z = "zzz";
     133    for (i; 5) {
     134        while ( bytesRemaining() > 10 ) {
     135            z += ".";
     136        }
     137        sout | "about to expand, a = " | a;
     138        while ( bytesRemaining() <= 10 ) {
     139            z += ".";
     140        }
     141        sout | "expanded, a = " | a;
     142
     143        // each growth gives more usable space than the last
     144        assert( bytesRemaining() > lastTimeBytesAvail );
     145        lastTimeBytesAvail = bytesRemaining();
     146    }
     147}
     148
    122149int main() {
    123150    basicFillCompact();
    124151    fillCompact_withSharedEdits();
     152    fillNoCompact();
    125153}
  • tests/collections/string-overwrite.cfa

    rdb1ebed r5235d49  
    11#include <containers/string.hfa>
     2#include <string_sharectx.hfa>
    23
    34/*
     
    1112WE = witness end
    1213
    13 The dest does:
     14The test does:
    1415  starts with the entire string being, initially, the alphabet; prints this entire alphabet
    1516  sets up modifier and witness as ranges within it, and prints a visualization of those ranges
     
    2425This API's convention has Start positions being inclusive and end positions being exclusive.
    2526
     27                                v Case number in output
    2628With 1 equivalence class:
    2729MS = ME = WS = WE               1
     
    118120    struct { int ms; int me; int ws; int we; char *replaceWith; char *label; } cases[] = {
    119121        { 12, 14, 10, 20, "xxxxx", "warmup" },
    120 //        { 12, 14, 12, 14, "xxxxx", ""       },  // the bug that got me into this test (should be a dup with case 6)
    121122        { 10, 10, 10, 10, "=====", "1"      },
    122123        { 10, 10, 10, 10, "=="   , ""       },
     
    223224        { 12, 14, 10, 16, "="    , ""       },
    224225        { 12, 14, 10, 16, ""     , ""       },
    225 /*
    226         { , , , , "=====", "NN"     },
    227         {  "=="   , ""       },
    228         {  "="    , ""       },
    229         {  ""     , ""       },
    230 */
    231226    };
    232227    for ( i; sizeof(cases)/sizeof(cases[0]) ) {
     
    238233
    239234
    240 // void f( string & s, string & toEdit ) {
    241 
    242 //     sout | s | "|" | toEdit | "|";
    243 
    244 //     s(14, 16) = "-";
    245 //     sout | s | "|" | toEdit | "|";
    246 // }
    247 
    248235int main() {
     236
     237    #ifdef STRING_SHARING_OFF
     238    string_sharectx c = { NO_SHARING };
     239    #endif
     240
     241
    249242    //          0         1         2
    250243    //          01234567890123456789012345
Note: See TracChangeset for help on using the changeset viewer.