Ignore:
File:
1 edited

Legend:

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

    r714e206 r5764204  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sat Feb 10 17:47:22 2024
    13 // Update Count     : 83
     12// Last Modified On : Mon Jan 22 23:12:42 2024
     13// Update Count     : 43
    1414//
    1515
     
    2525
    2626#include <assert.h>
    27 #include <complex.h>                                               // creal, cimag
     27#include <complex.h>                           // creal, cimag
    2828
    2929//######################### VbyteHeap "header" #########################
     
    3434
    3535struct VbyteHeap {
    36         int NoOfCompactions;                                                            // number of compactions of the byte area
    37         int NoOfExtensions;                                                                     // number of extensions in the size of the byte area
    38         int NoOfReductions;                                                                     // number of reductions in the size of the byte area
    39        
    40         int InitSize;                                                                           // initial number of bytes in the byte-string area
    41         int CurrSize;                                                                           // current number of bytes in the byte-string area
    42         char *StartVbyte;                                                                       // pointer to the `st byte of the start of the byte-string area
    43         char *EndVbyte;                                                                         // pointer to the next byte after the end of the currently used portion of byte-string area
    44         void *ExtVbyte;                                                                         // pointer to the next byte after the end of the byte-string area
    45 
    46         HandleNode Header;                                                                      // header node for handle list
     36    int NoOfCompactions;                                                // number of compactions of the byte area
     37    int NoOfExtensions;                                                 // number of extensions in the size of the byte area
     38    int NoOfReductions;                                                 // number of reductions in the size of the byte area
     39   
     40    int InitSize;                                                               // initial number of bytes in the byte-string area
     41    int CurrSize;                                                               // current number of bytes in the byte-string area
     42    char *StartVbyte;                                                   // pointer to the `st byte of the start of the byte-string area
     43    char *EndVbyte;                                                             // pointer to the next byte after the end of the currently used portion of byte-string area
     44    void *ExtVbyte;                                                             // pointer to the next byte after the end of the byte-string area
     45
     46    HandleNode Header;                                                  // header node for handle list
    4747}; // VbyteHeap
    4848
    49 
    50 static void compaction( VbyteHeap & );                                  // compaction of the byte area
    51 static void garbage( VbyteHeap &, int );                                // garbage collect the byte area
    52 static void extend( VbyteHeap &, int );                                 // extend the size of the byte area
    53 static void reduce( VbyteHeap &, int );                                 // reduce the size of the byte area
     49   
     50static void compaction( VbyteHeap & );                  // compaction of the byte area
     51static void garbage( VbyteHeap &, int );                // garbage collect the byte area
     52static void extend( VbyteHeap &, int );                 // extend the size of the byte area
     53static void reduce( VbyteHeap &, int );                 // reduce the size of the byte area
    5454
    5555static void ?{}( VbyteHeap &, size_t = 1000 );
    5656static void ^?{}( VbyteHeap & );
    5757
    58 static int ByteCmp( char *, int, int, char *, int, int ); // compare 2 blocks of bytes
     58static int ByteCmp( char *, int, int, char *, int, int );       // compare 2 blocks of bytes
    5959static char *VbyteAlloc( VbyteHeap &, int );                    // allocate a block bytes in the heap
    6060static char *VbyteTryAdjustLast( VbyteHeap &, int );
     
    6262static void AddThisAfter( HandleNode &, HandleNode & );
    6363static void DeleteNode( HandleNode & );
    64 static void MoveThisAfter( HandleNode &, const HandleNode & ); // move current handle after parameter handle
     64static void MoveThisAfter( HandleNode &, const HandleNode & );          // move current handle after parameter handle
    6565
    6666
     
    6969static void ?{}( VbyteHeap & s, size_t Size ) with(s) {
    7070#ifdef VbyteDebug
    71         serr | "enter:VbyteHeap::VbyteHeap, s:" | &s | " Size:" | Size;
    72 #endif // VbyteDebug
    73         NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
    74         InitSize = CurrSize = Size;
    75         StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    76         ExtVbyte = (void *)( StartVbyte + CurrSize );
    77         Header.flink = Header.blink = &Header;
    78         Header.ulink = &s;
    79 #ifdef VbyteDebug
    80         HeaderPtr = &Header;
    81         serr | "exit:VbyteHeap::VbyteHeap, s:" | &s;
     71    serr | "enter:VbyteHeap::VbyteHeap, s:" | &s | " Size:" | Size;
     72#endif // VbyteDebug
     73    NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
     74    InitSize = CurrSize = Size;
     75    StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     76    ExtVbyte = (void *)( StartVbyte + CurrSize );
     77    Header.flink = Header.blink = &Header;
     78    Header.ulink = &s;
     79#ifdef VbyteDebug
     80    HeaderPtr = &Header;
     81    serr | "exit:VbyteHeap::VbyteHeap, s:" | &s;
    8282#endif // VbyteDebug
    8383} // VbyteHeap
     
    8787
    8888static void ^?{}( VbyteHeap & s ) with(s) {
    89         free( StartVbyte );
     89    free( StartVbyte );
    9090} // ~VbyteHeap
    9191
     
    9999static void ?{}( HandleNode & s ) with(s) {
    100100#ifdef VbyteDebug
    101         serr | "enter:HandleNode::HandleNode, s:" | &s;
    102 #endif // VbyteDebug
    103         s = 0;
    104         lnth = 0;
    105 #ifdef VbyteDebug
    106         serr | "exit:HandleNode::HandleNode, s:" | &s;
     101    serr | "enter:HandleNode::HandleNode, s:" | &s;
     102#endif // VbyteDebug
     103    s = 0;
     104    lnth = 0;
     105#ifdef VbyteDebug
     106    serr | "exit:HandleNode::HandleNode, s:" | &s;
    107107#endif // VbyteDebug
    108108} // HandleNode
     
    114114static void ?{}( HandleNode & s, VbyteHeap & vh ) with(s) {
    115115#ifdef VbyteDebug
    116         serr | "enter:HandleNode::HandleNode, s:" | &s;
    117 #endif // VbyteDebug
    118         s = 0;
    119         lnth = 0;
    120         ulink = &vh;
    121         AddThisAfter( s, *vh.Header.blink );
    122 #ifdef VbyteDebug
    123         serr | "exit:HandleNode::HandleNode, s:" | &s;
     116    serr | "enter:HandleNode::HandleNode, s:" | &s;
     117#endif // VbyteDebug
     118    s = 0;
     119    lnth = 0;
     120    ulink = &vh;
     121    AddThisAfter( s, *vh.Header.blink );
     122#ifdef VbyteDebug
     123    serr | "exit:HandleNode::HandleNode, s:" | &s;
    124124#endif // VbyteDebug
    125125} // HandleNode
     
    131131static void ^?{}( HandleNode & s ) with(s) {
    132132#ifdef VbyteDebug
    133         serr | "enter:HandleNode::~HandleNode, s:" | & s;
    134         {
    135                 serr | nlOff;
    136                 serr | " lnth:" | lnth | " s:" | (void *)s | ",\"";
    137                 for ( i; lnth ) {
    138                         serr | s[i];
    139                 } // for
    140                 serr | "\" flink:" | flink | " blink:" | blink | nl;
    141                 serr | nlOn;
    142         }
    143 #endif // VbyteDebug
    144         DeleteNode( s );
     133    serr | "enter:HandleNode::~HandleNode, s:" | & s;
     134    {
     135        serr | nlOff;
     136        serr | " lnth:" | lnth | " s:" | (void *)s | ",\"";
     137        for ( i; lnth ) {
     138            serr | s[i];
     139        } // for
     140        serr | "\" flink:" | flink | " blink:" | blink | nl;
     141        serr | nlOn;
     142    }
     143#endif // VbyteDebug
     144    DeleteNode( s );
    145145} // ~HandleNode
    146146
     
    148148//######################### String Sharing Context #########################
    149149
    150 static string_sharectx * ambient_string_sharectx;               // fickle top of stack
     150static string_sharectx * ambient_string_sharectx;               // fickle top of stack
    151151static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack
    152152
    153153void ?{}( string_sharectx & s, StringSharectx_Mode mode ) with( s ) {
    154         (older){ ambient_string_sharectx };
    155         if ( mode == NEW_SHARING ) {
    156                 (activeHeap){ new( (size_t) 1000 ) };
    157         } else {
    158                 verify( mode == NO_SHARING );
    159                 (activeHeap){ 0p };
    160         }
    161         ambient_string_sharectx = & s;
     154    (older){ ambient_string_sharectx };
     155    if ( mode == NEW_SHARING ) {
     156        (activeHeap){ new( (size_t) 1000 ) };
     157    } else {
     158        verify( mode == NO_SHARING );
     159        (activeHeap){ 0p };
     160    }
     161    ambient_string_sharectx = & s;
    162162}
    163163
    164164void ^?{}( string_sharectx & s ) with( s ) {
    165         if ( activeHeap ) delete( activeHeap );
    166 
    167         // unlink s from older-list starting from ambient_string_sharectx
    168         // usually, s==ambient_string_sharectx and the loop runs zero times
    169         string_sharectx *& c = ambient_string_sharectx;
    170         while ( c != &s ) &c = &c->older;                                       // find s
    171         c = s.older;                                                                            // unlink
     165    if ( activeHeap ) delete( activeHeap );
     166
     167    // unlink s from older-list starting from ambient_string_sharectx
     168    // usually, s==ambient_string_sharectx and the loop runs zero times
     169    string_sharectx *& c = ambient_string_sharectx;
     170    while ( c != &s ) &c = &c->older;              // find s
     171    c = s.older;                                   // unlink
    172172}
    173173
     
    176176
    177177VbyteHeap * DEBUG_string_heap() {
    178         assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
    179         return ambient_string_sharectx->activeHeap;
     178    assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
     179    return ambient_string_sharectx->activeHeap;
    180180}
    181181
    182182size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) {
    183         return ((char *)heap->ExtVbyte) - heap->EndVbyte;
     183    return ((char *)heap->ExtVbyte) - heap->EndVbyte;
    184184}
    185185
    186186size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) {
    187         return heap->CurrSize;
     187    return heap->CurrSize;
    188188}
    189189
    190190const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
    191         return heap->StartVbyte;
     191    return heap->StartVbyte;
    192192}
    193193
    194194// Returns the size of the string in bytes
    195195size_t size(const string_res & s) with(s) {
    196         return Handle.lnth;
     196    return Handle.lnth;
    197197}
    198198
     
    201201        // CFA string is NOT null terminated, so print exactly lnth characters in a minimum width of 0.
    202202        out | wd( 0, s.Handle.lnth, s.Handle.s ) | nonl;
    203         return out;
     203    return out;
    204204}
    205205
     
    210210// Input operator
    211211ifstream & ?|?(ifstream & in, string_res & s) {
    212         // Reading into a temp before assigning to s is near zero overhead in typical cases because of sharing.
    213         // If s is a substring of something larger, simple assignment takes care of that case correctly.
    214         // But directly reading a variable amount of text into the middle of a larger context is not practical.
    215         string_res temp;
    216 
    217         // Read in chunks.  Often, one chunk is enough.  Keep the string that accumulates chunks last in the heap,
    218         // so available room is rest of heap.  When a chunk fills the heap, force growth then take the next chunk.
    219         for (bool cont = true; cont; ) {
    220                 cont = false;
    221 
    222                 // Append dummy content to temp, forcing expansion when applicable (occurs always on subsequent loops)
    223                 // length 2 ensures room for at least one real char, plus scanf/pipe-cstr's null terminator
    224                 temp += "--";
    225                 assert( temp.Handle.ulink->EndVbyte == temp.Handle.s + temp.Handle.lnth );      // last in heap
    226 
    227                 // reset, to overwrite the appended "--"
    228                 temp.Handle.lnth -= 2;
    229                 temp.Handle.ulink->EndVbyte -= 2;
    230 
    231                 // rest of heap is available to read into
    232                 int lenReadable = (char *)temp.Handle.ulink->ExtVbyte - temp.Handle.ulink->EndVbyte;
    233                 assert (lenReadable >= 2);
    234 
    235                 // get bytes
    236                 try {
     212    // Reading into a temp before assigning to s is near zero overhead in typical cases because of sharing.
     213    // If s is a substring of something larger, simple assignment takes care of that case correctly.
     214    // But directly reading a variable amount of text into the middle of a larger context is not practical.
     215    string_res temp;
     216
     217    // Read in chunks.  Often, one chunk is enough.  Keep the string that accumulates chunks last in the heap,
     218    // so available room is rest of heap.  When a chunk fills the heap, force growth then take the next chunk.
     219    for (bool cont = true; cont; ) {
     220        cont = false;
     221
     222        // Append dummy content to temp, forcing expansion when applicable (occurs always on subsequent loops)
     223        // length 2 ensures room for at least one real char, plus scanf/pipe-cstr's null terminator
     224        temp += "--";
     225        assert( temp.Handle.ulink->EndVbyte == temp.Handle.s + temp.Handle.lnth );    // last in heap
     226
     227        // reset, to overwrite the appended "--"
     228        temp.Handle.lnth -= 2;
     229        temp.Handle.ulink->EndVbyte -= 2;
     230
     231        // rest of heap is available to read into
     232        int lenReadable = (char *)temp.Handle.ulink->ExtVbyte - temp.Handle.ulink->EndVbyte;
     233        assert (lenReadable >= 2);
     234
     235        // get bytes
     236        try {
    237237                        *(temp.Handle.ulink->EndVbyte) = '\0';   // pre-assign empty cstring
    238                         in | wdi( lenReadable, temp.Handle.ulink->EndVbyte );
    239                 } catch (cstring_length *) {
    240                         cont = true;
    241                 }
    242                 int lenWasRead = strlen(temp.Handle.ulink->EndVbyte);
    243 
    244                 // update metadata
    245                 temp.Handle.lnth += lenWasRead;
    246                 temp.Handle.ulink->EndVbyte += lenWasRead;
    247         }
     238            in | wdi( lenReadable, temp.Handle.ulink->EndVbyte );
     239        } catch (cstring_length *) {
     240            cont = true;
     241        }
     242        int lenWasRead = strlen(temp.Handle.ulink->EndVbyte);
     243
     244        // update metadata
     245        temp.Handle.lnth += lenWasRead;
     246        temp.Handle.ulink->EndVbyte += lenWasRead;
     247    }
    248248
    249249        if ( temp.Handle.lnth > 0 ) s = temp;
    250         return in;
    251 }
    252 
    253 ifstream & ?|?( ifstream & is, _Istream_Rquoted f ) with( f.rstr ) {
    254         if ( eof( is ) ) throwResume ExceptionInst( missing_data );
    255         int args;
    256   fini: {
    257                 char rfmt[5] = { ' ', delimiters[0], '%', 'n', '\0' };
    258                 int len = -1;                                                                   // may not be set in fmt
    259                 args = fmt( is, rfmt, &len );                                   // remove leading whitespace and quote
    260           if ( eof( is ) || len == -1 ) break fini;
    261 
    262                 // Change the remainder of the read into a getline by reseting the closing delimiter.
    263                 if ( delimiters[1] != '\0' ) {
    264                         delimiters[0] = delimiters[1];
    265                         delimiters[1] = '\0';
    266                 } // if
    267                 flags.delimiter = true;
    268                 return is | *(_Istream_Rstr *)&f;
    269         } // fini
    270         // read failed => no pattern match => set string to null
    271         if ( ! flags.ignore && s != 0p && args == 0 ) s[0] = '\0';
    272         if ( args == 1 && eof( is ) ) {                                         // data but scan ended at EOF
    273                 clear( is );                                                                    // => reset EOF => detect again on next read
    274         } // if
    275         return is;
     250    return in;
     251}
     252
     253void ?|?( ifstream & in, string_res & s ) {
     254    (ifstream &)(in | s);
    276255}
    277256
     
    316295} // ?|?
    317296
     297void ?|?( ifstream & in, _Istream_Rstr f ) {
     298    (ifstream &)(in | f);
     299}
     300
     301
    318302// Empty constructor
    319303void ?{}(string_res & s) with(s) {
    320         if( ambient_string_sharectx->activeHeap ) {
    321                 (Handle){ * ambient_string_sharectx->activeHeap };
    322                 (shareEditSet_owns_ulink){ false };
    323                 verify( Handle.s == 0p && Handle.lnth == 0 );
    324         } else {
    325                 (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
    326                 (shareEditSet_owns_ulink){ true };
    327                 Handle.s = Handle.ulink->StartVbyte;
    328                 verify( Handle.lnth == 0 );
    329         }
    330         s.shareEditSet_prev = &s;
    331         s.shareEditSet_next = &s;
    332                 }
     304    if( ambient_string_sharectx->activeHeap ) {
     305        (Handle){ * ambient_string_sharectx->activeHeap };
     306        (shareEditSet_owns_ulink){ false };
     307        verify( Handle.s == 0p && Handle.lnth == 0 );
     308    } else {
     309        (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
     310        (shareEditSet_owns_ulink){ true };
     311        Handle.s = Handle.ulink->StartVbyte;
     312        verify( Handle.lnth == 0 );
     313    }
     314    s.shareEditSet_prev = &s;
     315    s.shareEditSet_next = &s;
     316}
    333317
    334318static void eagerCopyCtorHelper(string_res & s, const char * rhs, size_t rhslnth) with(s) {
    335         if( ambient_string_sharectx->activeHeap ) {
    336                 (Handle){ * ambient_string_sharectx->activeHeap };
    337                 (shareEditSet_owns_ulink){ false };
    338         } else {
    339                 (Handle){ * new( rhslnth ) };
    340                 (shareEditSet_owns_ulink){ true };
    341         }
    342         Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    343         Handle.lnth = rhslnth;
    344         memmove( Handle.s, rhs, rhslnth );
    345         s.shareEditSet_prev = &s;
    346         s.shareEditSet_next = &s;
     319    if( ambient_string_sharectx->activeHeap ) {
     320        (Handle){ * ambient_string_sharectx->activeHeap };
     321        (shareEditSet_owns_ulink){ false };
     322    } else {
     323        (Handle){ * new( rhslnth ) };
     324        (shareEditSet_owns_ulink){ true };
     325    }
     326    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     327    Handle.lnth = rhslnth;
     328    memmove( Handle.s, rhs, rhslnth );
     329    s.shareEditSet_prev = &s;
     330    s.shareEditSet_next = &s;
    347331}
    348332
    349333// Constructor from a raw buffer and size
    350334void ?{}(string_res & s, const char * rhs, size_t rhslnth) with(s) {
    351         eagerCopyCtorHelper(s, rhs, rhslnth);
     335    eagerCopyCtorHelper(s, rhs, rhslnth);
    352336}
    353337
    354338void ?{}( string_res & s, ssize_t rhs ) {
    355         char buf[64];
    356         int len;
    357         snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
    358         ( s ){ buf, len };
     339    char buf[64];
     340    int len;
     341    snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
     342    ( s ){ buf, len };
    359343}
    360344void ?{}( string_res & s, size_t rhs ) {
    361         char buf[64];
    362         int len;
    363         snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
    364         ( s ){ buf, len };
     345    char buf[64];
     346    int len;
     347    snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
     348    ( s ){ buf, len };
    365349}
    366350void ?{}( string_res & s, double rhs ) {
    367         char buf[64];
    368         int len;
    369         snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
    370         ( s ){ buf, len };
     351    char buf[64];
     352    int len;
     353    snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
     354    ( s ){ buf, len };
    371355}
    372356void ?{}( string_res & s, long double rhs ) {
    373         char buf[64];
    374         int len;
    375         snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
    376         ( s ){ buf, len };
     357    char buf[64];
     358    int len;
     359    snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
     360    ( s ){ buf, len };
    377361}
    378362void ?{}( string_res & s, double _Complex rhs ) {
    379         char buf[64];
    380         int len;
    381         snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len );
    382         ( s ){ buf, len };
     363    char buf[64];
     364    int len;
     365    snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len );
     366    ( s ){ buf, len };
    383367}
    384368void ?{}( string_res & s, long double _Complex rhs ) {
    385         char buf[64];
    386         int len;
    387         snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len );
    388         ( s ){ buf, len };
     369    char buf[64];
     370    int len;
     371    snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len );
     372    ( s ){ buf, len };
    389373}
    390374
    391375// private ctor (not in header): use specified heap (ignore ambient) and copy chars in
    392376void ?{}( string_res & s, VbyteHeap & heap, const char * rhs, size_t rhslnth ) with(s) {
    393         (Handle){ heap };
    394         Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
    395         Handle.lnth = rhslnth;
    396         (s.shareEditSet_owns_ulink){ false };
    397         memmove( Handle.s, rhs, rhslnth );
    398         s.shareEditSet_prev = &s;
    399         s.shareEditSet_next = &s;
     377    (Handle){ heap };
     378    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     379    Handle.lnth = rhslnth;
     380    (s.shareEditSet_owns_ulink){ false };
     381    memmove( Handle.s, rhs, rhslnth );
     382    s.shareEditSet_prev = &s;
     383    s.shareEditSet_next = &s;
    400384}
    401385
     
    403387// General copy constructor
    404388void ?{}(string_res & s, const string_res & s2, StrResInitMode mode, size_t start, size_t len ) {
    405         size_t end = start + len;
    406         verify( start <= end && end <= s2.Handle.lnth );
    407 
    408         if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
    409                 // crossing heaps (including private): copy eagerly
    410                 eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
    411                 verify(s.shareEditSet_prev == &s);
    412                 verify(s.shareEditSet_next == &s);
    413         } else {
    414                 (s.Handle){};
    415                 s.Handle.s = s2.Handle.s + start;
    416                 s.Handle.lnth = end - start;
    417                 s.Handle.ulink = s2.Handle.ulink;
    418 
    419                 AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
    420                 // ^ bug?  skip others at early point in string
    421 
    422                 if (mode == COPY_VALUE) {
    423                         verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
    424                         // requested logical copy in same heap: defer copy until write
    425 
    426                         (s.shareEditSet_owns_ulink){ false };
    427 
    428                         // make s alone in its shareEditSet
    429                         s.shareEditSet_prev = &s;
    430                         s.shareEditSet_next = &s;
    431                 } else {
    432                         verify( mode == SHARE_EDITS );
    433                         // sharing edits with source forces same heap as source (ignore context)
    434 
    435                         (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
    436 
    437                         // s2 is logically const but not implementation const
    438                         string_res & s2mod = (string_res &) s2;
    439 
    440                         // insert s after s2 on shareEditSet
    441                         s.shareEditSet_next = s2mod.shareEditSet_next;
    442                         s.shareEditSet_prev = &s2mod;
    443                         s.shareEditSet_next->shareEditSet_prev = &s;
    444                         s.shareEditSet_prev->shareEditSet_next = &s;
    445                 }
    446         }
     389
     390    size_t end = start + len;
     391    verify( start <= end && end <= s2.Handle.lnth );
     392
     393    if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
     394        // crossing heaps (including private): copy eagerly
     395        eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
     396        verify(s.shareEditSet_prev == &s);
     397        verify(s.shareEditSet_next == &s);
     398    } else {
     399        (s.Handle){};
     400        s.Handle.s = s2.Handle.s + start;
     401        s.Handle.lnth = end - start;
     402        s.Handle.ulink = s2.Handle.ulink;
     403
     404        AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
     405        // ^ bug?  skip others at early point in string
     406
     407        if (mode == COPY_VALUE) {
     408            verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
     409            // requested logical copy in same heap: defer copy until write
     410
     411            (s.shareEditSet_owns_ulink){ false };
     412
     413            // make s alone in its shareEditSet
     414            s.shareEditSet_prev = &s;
     415            s.shareEditSet_next = &s;
     416        } else {
     417            verify( mode == SHARE_EDITS );
     418            // sharing edits with source forces same heap as source (ignore context)
     419
     420            (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
     421
     422            // s2 is logically const but not implementation const
     423            string_res & s2mod = (string_res &) s2;
     424
     425            // insert s after s2 on shareEditSet
     426            s.shareEditSet_next = s2mod.shareEditSet_next;
     427            s.shareEditSet_prev = &s2mod;
     428            s.shareEditSet_next->shareEditSet_prev = &s;
     429            s.shareEditSet_prev->shareEditSet_next = &s;
     430        }
     431    }
    447432}
    448433
    449434static void assignEditSet(string_res & s, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
    450                                                   char * resultSesStart,
    451                                                   size_t resultSesLnth,
    452                                                   HandleNode * resultPadPosition, size_t bsize ) {
    453 
    454         char * beforeBegin = shareEditSetStartPeer->Handle.s;
    455         size_t beforeLen = s.Handle.s - beforeBegin;
    456 
    457         char * afterBegin = s.Handle.s + s.Handle.lnth;
    458         size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
    459 
    460         size_t oldLnth = s.Handle.lnth;
    461 
    462         s.Handle.s = resultSesStart + beforeLen;
    463         s.Handle.lnth = bsize;
    464         if (resultPadPosition)
    465                 MoveThisAfter( s.Handle, *resultPadPosition );
    466 
    467         // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
    468         char *limit = resultSesStart + resultSesLnth;
    469         for ( string_res * p = s.shareEditSet_next; p != &s; p = p->shareEditSet_next ) {
    470                 verify (p->Handle.s >= beforeBegin);
    471                 if ( p->Handle.s >= afterBegin ) {
    472                         verify ( p->Handle.s <= afterBegin + afterLen );
    473                         verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    474                         // p starts after the edit
    475                         // take start and end as end-anchored
    476                         size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
    477                         p->Handle.s = limit - startOffsetFromEnd;
    478                         // p->Handle.lnth unaffected
    479                 } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
    480                         // p starts before, or at the start of, the edit
    481                         if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
    482                                 // p ends before the edit
    483                                 // take end as start-anchored too
    484                                 // p->Handle.lnth unaffected
    485                         } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    486                                 // p ends during the edit; p does not include the last character replaced
    487                                 // clip end of p to end at start of edit
    488                                 p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
    489                         } else {
    490                                 // p ends after the edit
    491                                 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
    492                                 // take end as end-anchored
    493                                 // stretch-shrink p according to the edit
    494                                 p->Handle.lnth += s.Handle.lnth;
    495                                 p->Handle.lnth -= oldLnth;
    496                         }
    497                         // take start as start-anchored
    498                         size_t startOffsetFromStart = p->Handle.s - beforeBegin;
    499                         p->Handle.s = resultSesStart + startOffsetFromStart;
    500                 } else {
    501                         verify ( p->Handle.s < afterBegin );
    502                         // p starts during the edit
    503                         verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
    504                         if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
    505                                 // p ends during the edit; p does not include the last character replaced
    506                                 // set p to empty string at start of edit
    507                                 p->Handle.s = s.Handle.s;
    508                                 p->Handle.lnth = 0;
    509                         } else {
    510                                 // p includes the end of the edit
    511                                 // clip start of p to start at end of edit
    512                                 int charsToClip = afterBegin - p->Handle.s;
    513                                 p->Handle.s = s.Handle.s + s.Handle.lnth;
    514                                 p->Handle.lnth -= charsToClip;
    515                         }
    516                 }
    517                 if (resultPadPosition)
    518                         MoveThisAfter( p->Handle, *resultPadPosition ); // move substring handle to maintain sorted order by string position
    519         }
     435    char * resultSesStart,
     436    size_t resultSesLnth,
     437    HandleNode * resultPadPosition, size_t bsize ) {
     438
     439    char * beforeBegin = shareEditSetStartPeer->Handle.s;
     440    size_t beforeLen = s.Handle.s - beforeBegin;
     441
     442    char * afterBegin = s.Handle.s + s.Handle.lnth;
     443    size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
     444
     445    size_t oldLnth = s.Handle.lnth;
     446
     447    s.Handle.s = resultSesStart + beforeLen;
     448    s.Handle.lnth = bsize;
     449    if (resultPadPosition)
     450        MoveThisAfter( s.Handle, *resultPadPosition );
     451
     452    // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
     453    char *limit = resultSesStart + resultSesLnth;
     454    for ( string_res * p = s.shareEditSet_next; p != &s; p = p->shareEditSet_next ) {
     455        verify (p->Handle.s >= beforeBegin);
     456        if ( p->Handle.s >= afterBegin ) {
     457            verify ( p->Handle.s <= afterBegin + afterLen );
     458            verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     459            // p starts after the edit
     460            // take start and end as end-anchored
     461            size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
     462            p->Handle.s = limit - startOffsetFromEnd;
     463            // p->Handle.lnth unaffected
     464        } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
     465            // p starts before, or at the start of, the edit
     466            if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
     467                // p ends before the edit
     468                // take end as start-anchored too
     469                // p->Handle.lnth unaffected
     470            } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
     471                // p ends during the edit; p does not include the last character replaced
     472                // clip end of p to end at start of edit
     473                p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
     474            } else {
     475                // p ends after the edit
     476                verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     477                // take end as end-anchored
     478                // stretch-shrink p according to the edit
     479                p->Handle.lnth += s.Handle.lnth;
     480                p->Handle.lnth -= oldLnth;
     481            }
     482            // take start as start-anchored
     483            size_t startOffsetFromStart = p->Handle.s - beforeBegin;
     484            p->Handle.s = resultSesStart + startOffsetFromStart;
     485        } else {
     486            verify ( p->Handle.s < afterBegin );
     487            // p starts during the edit
     488            verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
     489            if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
     490                // p ends during the edit; p does not include the last character replaced
     491                // set p to empty string at start of edit
     492                p->Handle.s = s.Handle.s;
     493                p->Handle.lnth = 0;
     494            } else {
     495                // p includes the end of the edit
     496                // clip start of p to start at end of edit
     497                int charsToClip = afterBegin - p->Handle.s;
     498                p->Handle.s = s.Handle.s + s.Handle.lnth;
     499                p->Handle.lnth -= charsToClip;
     500            }
     501        }
     502        if (resultPadPosition)
     503            MoveThisAfter( p->Handle, *resultPadPosition );     // move substring handle to maintain sorted order by string position
     504    }
    520505}
    521506
    522507// traverse the share-edit set (SES) to recover the range of a base string to which `s` belongs
    523508static void locateInShareEditSet( string_res & s, string_res *& shareEditSetStartPeer, string_res *& shareEditSetEndPeer ) {
    524         shareEditSetStartPeer = & s;
    525         shareEditSetEndPeer = & s;
    526         for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) {
    527                 if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
    528                         shareEditSetStartPeer = editPeer;
    529                 }
    530                 if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
    531                         shareEditSetEndPeer = editPeer;
    532                 }
    533         }
     509    shareEditSetStartPeer = & s;
     510    shareEditSetEndPeer = & s;
     511    for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) {
     512        if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
     513            shareEditSetStartPeer = editPeer;
     514        }
     515        if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
     516            shareEditSetEndPeer = editPeer;
     517        }
     518    }
    534519}
    535520
    536521static string_res & assign_(string_res & s, const char * buffer, size_t bsize, const string_res & valSrc) {
    537         string_res * shareEditSetStartPeer;
    538         string_res * shareEditSetEndPeer;
    539         locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer );
    540 
    541         verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
    542         size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
    543         verify( origEditSetLength >= s.Handle.lnth );
    544 
    545         if ( s.shareEditSet_owns_ulink ) {                               // assigning to private context
    546                 // ok to overwrite old value within LHS
    547                 char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
    548                 int prefixLen = s.Handle.s - prefixStartOrig;
    549                 char * suffixStartOrig = s.Handle.s + s.Handle.lnth;
    550                 int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
    551 
    552                 int delta = bsize - s.Handle.lnth;
    553                 if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) {
    554                         // growing: copy from old to new
    555                         char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta );
    556                         char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
    557                         destCursor += prefixLen;  memcpy(destCursor, buffer              , bsize        );
    558                         destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
    559                         assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    560                                                   dest,
    561                                                   origEditSetLength + delta,
    562                                                   0p, bsize);
    563                         free( oldBytes );
    564                 } else {
    565                         // room is already allocated in-place: bubble suffix and overwite middle
    566                         memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
    567                         memcpy( s.Handle.s, buffer, bsize );
    568 
    569                         assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    570                                                   shareEditSetStartPeer->Handle.s,
    571                                                   origEditSetLength + delta,
    572                                                   0p, bsize);
    573                 }
    574 
    575         } else if (                                                                                // assigning to shared context
    576                 s.Handle.lnth == origEditSetLength &&             // overwriting entire run of SES
    577                 & valSrc &&                                                                        // sourcing from a managed string
    578                 valSrc.Handle.ulink == s.Handle.ulink  ) {       // sourcing from same heap
    579 
    580                 // SES's result will only use characters from the source string => reuse source
    581                 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    582                                           valSrc.Handle.s,
    583                                           valSrc.Handle.lnth,
    584                                           &((string_res&)valSrc).Handle, bsize);
    585                
    586         } else {
    587                 // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
    588                 // OR we are importing characters: need to copy eagerly (can't refer to source)
    589 
    590                 // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
    591                 // `s` occurs in the middle of it, to be replaced
    592                 // build up the new text in `pasting`
    593 
    594                 string_res pasting = {
    595                         * s.Handle.ulink,                                                          // maintain same heap, regardless of context
    596                         shareEditSetStartPeer->Handle.s,                                   // start of SES
    597                         s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s
    598                 append( pasting,
    599                                 buffer,                                                                                 // start of replacement for s
    600                                 bsize );                                                                                   // length of replacement for s
    601                 append( pasting,
    602                                 s.Handle.s + s.Handle.lnth,                               // start of SES after s
    603                                 shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
    604                                 (s.Handle.s + s.Handle.lnth) );                   // length of SES, after s
    605 
    606                 // The above string building can trigger compaction.
    607                 // The reference points (that are arguments of the string building) may move during that building.
    608                 // From s point on, they are stable.
    609 
    610                 assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
    611                                           pasting.Handle.s,
    612                                           pasting.Handle.lnth,
    613                                           &pasting.Handle, bsize);
    614         }
    615 
    616         return s;
     522
     523    string_res * shareEditSetStartPeer;
     524    string_res * shareEditSetEndPeer;
     525    locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer );
     526
     527    verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
     528    size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
     529    verify( origEditSetLength >= s.Handle.lnth );
     530
     531    if ( s.shareEditSet_owns_ulink ) {                 // assigning to private context
     532        // ok to overwrite old value within LHS
     533        char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
     534        int prefixLen = s.Handle.s - prefixStartOrig;
     535        char * suffixStartOrig = s.Handle.s + s.Handle.lnth;
     536        int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
     537
     538        int delta = bsize - s.Handle.lnth;
     539        if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) {
     540            // growing: copy from old to new
     541            char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta );
     542            char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
     543            destCursor += prefixLen;  memcpy(destCursor, buffer         , bsize    );
     544            destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
     545            assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     546                dest,
     547                origEditSetLength + delta,
     548                0p, bsize);
     549            free( oldBytes );
     550        } else {
     551            // room is already allocated in-place: bubble suffix and overwite middle
     552            memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
     553            memcpy( s.Handle.s, buffer, bsize );
     554
     555            assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     556                shareEditSetStartPeer->Handle.s,
     557                origEditSetLength + delta,
     558                0p, bsize);
     559        }
     560
     561    } else if (                                           // assigning to shared context
     562        s.Handle.lnth == origEditSetLength &&          // overwriting entire run of SES
     563        & valSrc &&                                       // sourcing from a managed string
     564        valSrc.Handle.ulink == s.Handle.ulink  ) {     // sourcing from same heap
     565
     566        // SES's result will only use characters from the source string => reuse source
     567        assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     568            valSrc.Handle.s,
     569            valSrc.Handle.lnth,
     570            &((string_res&)valSrc).Handle, bsize);
     571       
     572    } else {
     573        // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
     574        // OR we are importing characters: need to copy eagerly (can't refer to source)
     575
     576        // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
     577        // `s` occurs in the middle of it, to be replaced
     578        // build up the new text in `pasting`
     579
     580        string_res pasting = {
     581            * s.Handle.ulink,                               // maintain same heap, regardless of context
     582            shareEditSetStartPeer->Handle.s,                   // start of SES
     583            s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s
     584        append( pasting,
     585            buffer,                                            // start of replacement for s
     586            bsize );                                           // length of replacement for s
     587        append( pasting,
     588            s.Handle.s + s.Handle.lnth,                  // start of SES after s
     589            shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
     590            (s.Handle.s + s.Handle.lnth) );              // length of SES, after s
     591
     592        // The above string building can trigger compaction.
     593        // The reference points (that are arguments of the string building) may move during that building.
     594        // From s point on, they are stable.
     595
     596        assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     597            pasting.Handle.s,
     598            pasting.Handle.lnth,
     599            &pasting.Handle, bsize);
     600    }
     601
     602    return s;
    617603}
    618604
    619605string_res & assign(string_res & s, const string_res & src, size_t maxlen) {
    620         return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
     606    return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
    621607}
    622608
    623609string_res & assign(string_res & s, const char * buffer, size_t bsize) {
    624         return assign_(s, buffer, bsize, *0p);
     610    return assign_(s, buffer, bsize, *0p);
    625611}
    626612
    627613string_res & ?=?(string_res & s, char c) {
    628         return assign(s, &c, 1);
     614    return assign(s, &c, 1);
    629615}
    630616
    631617string_res & ?=?( string_res & s, ssize_t rhs ) {
    632         string_res rhs2 = rhs;
    633         s = rhs2;
    634         return s;
     618    string_res rhs2 = rhs;
     619    s = rhs2;
     620    return s;
    635621}
    636622string_res & ?=?( string_res & s, size_t rhs ) {
    637         string_res rhs2 = rhs;
    638         s = rhs2;
    639         return s;
     623    string_res rhs2 = rhs;
     624    s = rhs2;
     625    return s;
    640626}
    641627string_res & ?=?( string_res & s, double rhs ) {
    642         string_res rhs2 = rhs;
    643         s = rhs2;
    644         return s;
     628    string_res rhs2 = rhs;
     629    s = rhs2;
     630    return s;
    645631}
    646632string_res & ?=?( string_res & s, long double rhs ) {
    647         string_res rhs2 = rhs;
    648         s = rhs2;
    649         return s;
     633    string_res rhs2 = rhs;
     634    s = rhs2;
     635    return s;
    650636}
    651637string_res & ?=?( string_res & s, double _Complex rhs ) {
    652         string_res rhs2 = rhs;
    653         s = rhs2;
    654         return s;
     638    string_res rhs2 = rhs;
     639    s = rhs2;
     640    return s;
    655641}
    656642string_res & ?=?( string_res & s, long double _Complex rhs ) {
    657         string_res rhs2 = rhs;
    658         s = rhs2;
    659         return s;
     643    string_res rhs2 = rhs;
     644    s = rhs2;
     645    return s;
    660646}
    661647
    662648// Copy assignment operator
    663649string_res & ?=?(string_res & s, const string_res & rhs) with( s ) {
    664         return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
     650    return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
    665651}
    666652
    667653string_res & ?=?(string_res & s, string_res & rhs) with( s ) {
    668         const string_res & rhs2 = rhs;
    669         return s = rhs2;
     654    const string_res & rhs2 = rhs;
     655    return s = rhs2;
    670656}
    671657
     
    673659// Destructor
    674660void ^?{}(string_res & s) with(s) {
    675         // much delegated to implied ^VbyteSM
    676 
    677         // sever s from its share-edit peers, if any (four no-ops when already solo)
    678         s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
    679         s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
    680         // s.shareEditSet_next = &s;
    681         // s.shareEditSet_prev = &s;
    682 
    683         if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
    684                 delete( s.Handle.ulink );
    685         }
     661    // much delegated to implied ^VbyteSM
     662
     663    // sever s from its share-edit peers, if any (four no-ops when already solo)
     664    s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
     665    s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
     666    // s.shareEditSet_next = &s;
     667    // s.shareEditSet_prev = &s;
     668
     669    if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
     670        delete( s.Handle.ulink );
     671    }
    686672}
    687673
     
    691677// offset from the start of the string.
    692678char ?[?](const string_res & s, size_t index) with(s) {
    693         //TODO: Check if index is valid (no exceptions yet)
    694         return Handle.s[index];
     679    //TODO: Check if index is valid (no exceptions yet)
     680    return Handle.s[index];
    695681}
    696682
    697683void assignAt(const string_res & s, size_t index, char val) {
    698         // caution: not tested (not reachable by string-api-coverage interface)
    699         // equivalent form at string level is `s[index] = val`,
    700         // which uses the overload that returns a length-1 string
    701         string_res editZone = { s, SHARE_EDITS, index, 1 };
    702         assign(editZone, &val, 1);
     684    // caution: not tested (not reachable by string-api-coverage interface)
     685    // equivalent form at string level is `s[index] = val`,
     686    // which uses the overload that returns a length-1 string
     687    string_res editZone = { s, SHARE_EDITS, index, 1 };
     688    assign(editZone, &val, 1);
    703689}
    704690
     
    708694
    709695void append(string_res & str1, const char * buffer, size_t bsize) {
    710         size_t clnth = str1.Handle.lnth + bsize;
    711         if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
    712                 // no-op
    713         } else {                                                                                        // must copy some text
    714                 if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
    715                         VbyteAlloc( *str1.Handle.ulink, bsize );        // create room for 2nd part at the end of string area
    716                 } else {                                                                                // copy the two parts
    717                         char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
    718                         char * str1oldBuf = str1.Handle.s;                      // must read after VbyteAlloc call in case it gs's
    719                         str1.Handle.s = str1newBuf;
    720                         memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
    721                 } // if
    722                 memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
    723         } // if
    724         str1.Handle.lnth = clnth;
     696    size_t clnth = str1.Handle.lnth + bsize;
     697    if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
     698        // no-op
     699    } else {                                            // must copy some text
     700        if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
     701            VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
     702        } else {                                        // copy the two parts
     703            char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
     704            char * str1oldBuf = str1.Handle.s;  // must read after VbyteAlloc call in case it gs's
     705            str1.Handle.s = str1newBuf;
     706            memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
     707        } // if
     708        memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
     709    } // if
     710    str1.Handle.lnth = clnth;
    725711}
    726712
    727713void ?+=?(string_res & str1, const string_res & str2) {
    728         append( str1, str2.Handle.s, str2.Handle.lnth );
     714    append( str1, str2.Handle.s, str2.Handle.lnth );
    729715}
    730716
    731717void append(string_res & str1, const string_res & str2, size_t maxlen) {
    732         append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
     718    append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
    733719}
    734720
    735721void ?+=?(string_res & s, char c) {
    736         append( s, & c, 1 );
     722    append( s, & c, 1 );
    737723}
    738724void ?+=?(string_res & s, const char * c) {
    739         append( s, c, strlen(c) );
     725    append( s, c, strlen(c) );
    740726}
    741727
     
    744730
    745731void ?*=?(string_res & s, size_t factor) {
    746         string_res s2 = { s, COPY_VALUE };
    747         s = "";
    748         for (factor) s += s2;
     732    string_res s2 = { s, COPY_VALUE };
     733    s = "";
     734    for (factor) s += s2;
    749735}
    750736
     
    753739
    754740int strcmp(const string_res & s1, const string_res & s2) {
    755         // return 0;
    756         int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth));
    757         if (ans1 != 0) return ans1;
    758         return s1.Handle.lnth - s2.Handle.lnth;
     741    // return 0;
     742    int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth));
     743    if (ans1 != 0) return ans1;
     744    return s1.Handle.lnth - s2.Handle.lnth;
    759745}
    760746
     
    767753
    768754int strcmp (const string_res & s1, const char * s2) {
    769         string_res s2x = s2;
    770         return strcmp(s1, s2x);
     755    string_res s2x = s2;
     756    return strcmp(s1, s2x);
    771757}
    772758
     
    779765
    780766int strcmp (const char * s1, const string_res & s2) {
    781         string_res s1x = s1;
    782         return strcmp(s1x, s2);
     767    string_res s1x = s1;
     768    return strcmp(s1x, s2);
    783769}
    784770
     
    791777
    792778
     779
    793780//////////////////////////////////////////////////////////
    794781// Search
    795782
    796783bool contains(const string_res & s, char ch) {
    797         for ( i; size(s) ) {
    798                 if (s[i] == ch) return true;
    799         }
    800         return false;
     784    for ( i; size(s) ) {
     785        if (s[i] == ch) return true;
     786    }
     787    return false;
    801788}
    802789
    803790int find(const string_res & s, char search) {
    804         return findFrom(s, 0, search);
     791    return findFrom(s, 0, search);
    805792}
    806793
    807794int findFrom(const string_res & s, size_t fromPos, char search) {
    808         // FIXME: This paricular overload (find of single char) is optimized to use memchr.
    809         // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
    810         char * searchFrom = s.Handle.s + fromPos;
    811         size_t searchLnth = s.Handle.lnth - fromPos;
    812         int searchVal = search;
    813         char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
    814         if (foundAt == 0p) return s.Handle.lnth;
    815         else return foundAt - s.Handle.s;
     795    // FIXME: This paricular overload (find of single char) is optimized to use memchr.
     796    // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
     797    char * searchFrom = s.Handle.s + fromPos;
     798    size_t searchLnth = s.Handle.lnth - fromPos;
     799    int searchVal = search;
     800    char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
     801    if (foundAt == 0p) return s.Handle.lnth;
     802    else return foundAt - s.Handle.s;
    816803}
    817804
    818805int find(const string_res & s, const string_res & search) {
    819         return findFrom(s, 0, search);
     806    return findFrom(s, 0, search);
    820807}
    821808
    822809int findFrom(const string_res & s, size_t fromPos, const string_res & search) {
    823         return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
     810    return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
    824811}
    825812
    826813int find(const string_res & s, const char * search) {
    827         return findFrom(s, 0, search);
     814    return findFrom(s, 0, search);
    828815}
    829816int findFrom(const string_res & s, size_t fromPos, const char * search) {
    830         return findFrom(s, fromPos, search, strlen(search));
     817    return findFrom(s, fromPos, search, strlen(search));
    831818}
    832819
    833820int find(const string_res & s, const char * search, size_t searchsize) {
    834         return findFrom(s, 0, search, searchsize);
     821    return findFrom(s, 0, search, searchsize);
    835822}
    836823
    837824int findFrom(const string_res & s, size_t fromPos, const char * search, size_t searchsize) {
    838         /* Remaining implementations essentially ported from Sunjay's work */
    839 
    840         // FIXME: This is a naive algorithm. We probably want to switch to someting
    841         // like Boyer-Moore in the future.
    842         // https://en.wikipedia.org/wiki/String_searching_algorithm
    843 
    844         // Always find the empty string
    845         if (searchsize == 0) {
    846                 return 0;
    847         }
    848 
    849         for ( i; fromPos ~ s.Handle.lnth ) {
    850                 size_t remaining = s.Handle.lnth - i;
    851                 // Never going to find the search string if the remaining string is
    852                 // smaller than search
    853                 if (remaining < searchsize) {
    854                         break;
    855                 }
    856 
    857                 bool matched = true;
    858                 for ( j; searchsize ) {
    859                         if (search[j] != s.Handle.s[i + j]) {
    860                                 matched = false;
    861                                 break;
    862                         }
    863                 }
    864                 if (matched) {
    865                         return i;
    866                 }
    867         }
    868         return s.Handle.lnth;
     825
     826    /* Remaining implementations essentially ported from Sunjay's work */
     827
     828
     829    // FIXME: This is a naive algorithm. We probably want to switch to someting
     830    // like Boyer-Moore in the future.
     831    // https://en.wikipedia.org/wiki/String_searching_algorithm
     832
     833    // Always find the empty string
     834    if (searchsize == 0) {
     835        return 0;
     836    }
     837
     838    for ( i; fromPos ~ s.Handle.lnth ) {
     839        size_t remaining = s.Handle.lnth - i;
     840        // Never going to find the search string if the remaining string is
     841        // smaller than search
     842        if (remaining < searchsize) {
     843            break;
     844        }
     845
     846        bool matched = true;
     847        for ( j; searchsize ) {
     848            if (search[j] != s.Handle.s[i + j]) {
     849                matched = false;
     850                break;
     851            }
     852        }
     853        if (matched) {
     854            return i;
     855        }
     856    }
     857
     858    return s.Handle.lnth;
    869859}
    870860
    871861bool includes(const string_res & s, const string_res & search) {
    872         return includes(s, search.Handle.s, search.Handle.lnth);
     862    return includes(s, search.Handle.s, search.Handle.lnth);
    873863}
    874864
    875865bool includes(const string_res & s, const char * search) {
    876         return includes(s, search, strlen(search));
     866    return includes(s, search, strlen(search));
    877867}
    878868
    879869bool includes(const string_res & s, const char * search, size_t searchsize) {
    880         return find(s, search, searchsize) < s.Handle.lnth;
     870    return find(s, search, searchsize) < s.Handle.lnth;
    881871}
    882872
    883873bool startsWith(const string_res & s, const string_res & prefix) {
    884         return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
     874    return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
    885875}
    886876
    887877bool startsWith(const string_res & s, const char * prefix) {
    888         return startsWith(s, prefix, strlen(prefix));
     878    return startsWith(s, prefix, strlen(prefix));
    889879}
    890880
    891881bool startsWith(const string_res & s, const char * prefix, size_t prefixsize) {
    892         if (s.Handle.lnth < prefixsize) {
    893                 return false;
    894         }
    895         return memcmp(s.Handle.s, prefix, prefixsize) == 0;
     882    if (s.Handle.lnth < prefixsize) {
     883        return false;
     884    }
     885    return memcmp(s.Handle.s, prefix, prefixsize) == 0;
    896886}
    897887
    898888bool endsWith(const string_res & s, const string_res & suffix) {
    899         return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
     889    return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
    900890}
    901891
    902892bool endsWith(const string_res & s, const char * suffix) {
    903         return endsWith(s, suffix, strlen(suffix));
     893    return endsWith(s, suffix, strlen(suffix));
    904894}
    905895
    906896bool endsWith(const string_res & s, const char * suffix, size_t suffixsize) {
    907         if (s.Handle.lnth < suffixsize) {
    908                 return false;
    909         }
    910         // Amount to offset the bytes pointer so that we are comparing the end of s
    911         // to suffix. s.bytes + offset should be the first byte to compare against suffix
    912         size_t offset = s.Handle.lnth - suffixsize;
    913         return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
    914 }
    915 
    916 /* Back to Mike's work */
     897    if (s.Handle.lnth < suffixsize) {
     898        return false;
     899    }
     900    // Amount to offset the bytes pointer so that we are comparing the end of s
     901    // to suffix. s.bytes + offset should be the first byte to compare against suffix
     902    size_t offset = s.Handle.lnth - suffixsize;
     903    return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
     904}
     905
     906    /* Back to Mike's work */
     907
    917908
    918909///////////////////////////////////////////////////////////////////////////
     
    920911
    921912void ?{}( charclass_res & s, const string_res & chars) {
    922         (s){ chars.Handle.s, chars.Handle.lnth };
     913    (s){ chars.Handle.s, chars.Handle.lnth };
    923914}
    924915
    925916void ?{}( charclass_res & s, const char * chars ) {
    926         (s){ chars, strlen(chars) };
     917    (s){ chars, strlen(chars) };
    927918}
    928919
    929920void ?{}( charclass_res & s, const char * chars, size_t charssize ) {
    930         (s.chars){ chars, charssize };
    931         // now sort it ?
     921    (s.chars){ chars, charssize };
     922    // now sort it ?
    932923}
    933924
    934925void ^?{}( charclass_res & s ) {
    935         ^(s.chars){};
     926    ^(s.chars){};
    936927}
    937928
    938929static bool test( const charclass_res & mask, char c ) {
    939         // instead, use sorted char list?
    940         return contains( mask.chars, c );
     930    // instead, use sorted char list?
     931    return contains( mask.chars, c );
    941932}
    942933
    943934int exclude(const string_res & s, const charclass_res & mask) {
    944         for ( i; size(s) ) {
    945                 if ( test(mask, s[i]) ) return i;
    946         }
    947         return size(s);
     935    for ( i; size(s) ) {
     936        if ( test(mask, s[i]) ) return i;
     937    }
     938    return size(s);
    948939}
    949940
    950941int include(const string_res & s, const charclass_res & mask) {
    951         for ( i; size(s) ) {
    952                 if ( ! test(mask, s[i]) ) return i;
    953         }
    954         return size(s);
     942    for ( i; size(s) ) {
     943        if ( ! test(mask, s[i]) ) return i;
     944    }
     945    return size(s);
    955946}
    956947
     
    962953static void AddThisAfter( HandleNode & s, HandleNode & n ) with(s) {
    963954#ifdef VbyteDebug
    964         serr | "enter:AddThisAfter, s:" | &s | " n:" | &n;
    965 #endif // VbyteDebug
    966         // 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).
    967         verify( n.ulink != 0p );
    968         verify( s.ulink == n.ulink );
    969         flink = n.flink;
    970         blink = &n;
    971         n.flink->blink = &s;
    972         n.flink = &s;
    973 #ifdef VbyteDebug
    974         {
     955    serr | "enter:AddThisAfter, s:" | &s | " n:" | &n;
     956#endif // VbyteDebug
     957    // 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).
     958    verify( n.ulink != 0p );
     959    verify( s.ulink == n.ulink );
     960    flink = n.flink;
     961    blink = &n;
     962    n.flink->blink = &s;
     963    n.flink = &s;
     964#ifdef VbyteDebug
     965    {
    975966                serr | "HandleList:";
    976967                serr | nlOff;
     
    983974                } // for
    984975                serr | nlOn;
    985         }
    986         serr | "exit:AddThisAfter";
     976    }
     977    serr | "exit:AddThisAfter";
    987978#endif // VbyteDebug
    988979} // AddThisAfter
     
    993984static void DeleteNode( HandleNode & s ) with(s) {
    994985#ifdef VbyteDebug
    995         serr | "enter:DeleteNode, s:" | &s;
    996 #endif // VbyteDebug
    997         flink->blink = blink;
    998         blink->flink = flink;
    999 #ifdef VbyteDebug
    1000         serr | "exit:DeleteNode";
     986    serr | "enter:DeleteNode, s:" | &s;
     987#endif // VbyteDebug
     988    flink->blink = blink;
     989    blink->flink = flink;
     990#ifdef VbyteDebug
     991    serr | "exit:DeleteNode";
    1001992#endif // VbyteDebug
    1002993} //  DeleteNode
     
    1008999static char * VbyteAlloc( VbyteHeap & s, int size ) with(s) {
    10091000#ifdef VbyteDebug
    1010         serr | "enter:VbyteAlloc, size:" | size;
    1011 #endif // VbyteDebug
    1012         uintptr_t NoBytes;
    1013         char *r;
    1014 
    1015         NoBytes = ( uintptr_t )EndVbyte + size;
    1016         if ( NoBytes > ( uintptr_t )ExtVbyte ) {                        // enough room for new byte-string ?
     1001    serr | "enter:VbyteAlloc, size:" | size;
     1002#endif // VbyteDebug
     1003    uintptr_t NoBytes;
     1004    char *r;
     1005
     1006    NoBytes = ( uintptr_t )EndVbyte + size;
     1007    if ( NoBytes > ( uintptr_t )ExtVbyte ) {                    // enough room for new byte-string ?
    10171008                garbage( s, size );                                                             // firer up the garbage collector
    10181009                verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
    1019         } // if
    1020         r = EndVbyte;
    1021         EndVbyte += size;
    1022 #ifdef VbyteDebug
    1023         serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
    1024 #endif // VbyteDebug
    1025         return r;
     1010    } // if
     1011    r = EndVbyte;
     1012    EndVbyte += size;
     1013#ifdef VbyteDebug
     1014    serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
     1015#endif // VbyteDebug
     1016    return r;
    10261017} // VbyteAlloc
    10271018
     
    10361027
    10371028static char * VbyteTryAdjustLast( VbyteHeap & s, int delta ) with(s) {
    1038         if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
    1039                 // room available
    1040                 EndVbyte += delta;
    1041                 return 0p;
    1042         }
    1043 
    1044         char *oldBytes = StartVbyte;
    1045 
    1046         NoOfExtensions += 1;
    1047         CurrSize *= 2;
    1048         StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    1049         ExtVbyte = StartVbyte + CurrSize;
    1050 
    1051         return oldBytes;
     1029    if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
     1030        // room available
     1031        EndVbyte += delta;
     1032        return 0p;
     1033    }
     1034
     1035    char *oldBytes = StartVbyte;
     1036
     1037    NoOfExtensions += 1;
     1038    CurrSize *= 2;
     1039    StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     1040    ExtVbyte = StartVbyte + CurrSize;
     1041
     1042    return oldBytes;
    10521043}
    10531044
     
    10581049static void MoveThisAfter( HandleNode & s, const HandleNode  & h ) with(s) {
    10591050#ifdef VbyteDebug
    1060         serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h;
    1061 #endif // VbyteDebug
    1062         verify( h.ulink != 0p );
    1063         verify( s.ulink == h.ulink );
    1064         if ( s < h.s ) {                                        // check argument values
     1051    serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h;
     1052#endif // VbyteDebug
     1053    verify( h.ulink != 0p );
     1054    verify( s.ulink == h.ulink );
     1055    if ( s < h.s ) {                                    // check argument values
    10651056                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    1066                 //        | ( h->s ) | " and keep handles in ascending order";
     1057                //      | ( h->s ) | " and keep handles in ascending order";
    10671058                // exit(-1 );
    10681059                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    1069         } // if
    1070 
    1071         HandleNode *i;
    1072         for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
    1073         if ( & s != i->blink ) {
     1060    } // if
     1061
     1062    HandleNode *i;
     1063    for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
     1064    if ( & s != i->blink ) {
    10741065                DeleteNode( s );
    10751066                AddThisAfter( s, *i->blink );
    1076         } // if
    1077 #ifdef VbyteDebug
    1078         {
    1079                 serr | "HandleList:";
    1080                 serr | nlOff;
    1081                 for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
    1082                         serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
    1083                         for ( i; n->lnth ) {
    1084                                 serr | n->s[i];
    1085                         } // for
    1086                         serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
    1087                 } // for
    1088                 serr | nlOn;
    1089         }
    1090         serr | "exit:MoveThisAfter";
     1067    } // if
     1068#ifdef VbyteDebug
     1069    {
     1070        serr | "HandleList:";
     1071        serr | nlOff;
     1072        for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
     1073            serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
     1074            for ( i; n->lnth ) {
     1075                        serr | n->s[i];
     1076            } // for
     1077            serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
     1078        } // for
     1079        serr | nlOn;
     1080    }
     1081    serr | "exit:MoveThisAfter";
    10911082#endif // VbyteDebug
    10921083} // MoveThisAfter
     1084
     1085
     1086
    10931087
    10941088
     
    11031097int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  {
    11041098#ifdef VbyteDebug
    1105         serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
    1106 #endif // VbyteDebug
    1107         int cmp;
    1108 
    1109   CharZip: for ( int i = 0; ; i += 1 ) {
    1110                 if ( i == Src2Lnth - 1 ) {
    1111                         for ( ; ; i += 1 ) {
    1112                                 if ( i == Src1Lnth - 1 ) {
    1113                                         cmp = 0;
    1114                                         break CharZip;
    1115                                 } // exit
    1116                                 if ( Src1[Src1Start + i] != ' ') {
    1117                                         // SUSPECTED BUG:  this could be be why Peter got the bug report about == " "  (why is this case here at all?)
    1118                                         cmp = 1;
    1119                                         break CharZip;
    1120                                 } // exit
    1121                         } // for
     1099    serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
     1100#endif // VbyteDebug
     1101    int cmp;
     1102
     1103    CharZip: for ( int i = 0; ; i += 1 ) {
     1104        if ( i == Src2Lnth - 1 ) {
     1105            for ( ; ; i += 1 ) {
     1106                if ( i == Src1Lnth - 1 ) {
     1107                    cmp = 0;
     1108                    break CharZip;
    11221109                } // exit
    1123                 if ( i == Src1Lnth - 1 ) {
    1124                         for ( ; ; i += 1 ) {
    1125                                 if ( i == Src2Lnth - 1 ) {
    1126                                         cmp = 0;
    1127                                         break CharZip;
    1128                                 } // exit
    1129                                 if ( Src2[Src2Start + i] != ' ') {
    1130                                         cmp = -1;
    1131                                         break CharZip;
    1132                                 } // exit
    1133                         } // for
     1110                if ( Src1[Src1Start + i] != ' ') {
     1111                        // SUSPECTED BUG:  this could be be why Peter got the bug report about == " "  (why is this case here at all?)
     1112                    cmp = 1;
     1113                    break CharZip;
    11341114                } // exit
    1135                 if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
    1136                         cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
    1137                         break CharZip;
     1115            } // for
     1116        } // exit
     1117        if ( i == Src1Lnth - 1 ) {
     1118            for ( ; ; i += 1 ) {
     1119                if ( i == Src2Lnth - 1 ) {
     1120                    cmp = 0;
     1121                    break CharZip;
    11381122                } // exit
    1139         } // for
    1140 #ifdef VbyteDebug
    1141         serr | "exit:ByteCmp, cmp:" | cmp;
    1142 #endif // VbyteDebug
    1143         return cmp;
     1123                if ( Src2[Src2Start + i] != ' ') {
     1124                    cmp = -1;
     1125                    break CharZip;
     1126                } // exit
     1127            } // for
     1128        } // exit
     1129      if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
     1130            cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
     1131            break CharZip;
     1132        } // exit
     1133    } // for
     1134#ifdef VbyteDebug
     1135    serr | "exit:ByteCmp, cmp:" | cmp;
     1136#endif // VbyteDebug
     1137    return cmp;
    11441138} // ByteCmp
    11451139
     
    11511145
    11521146void compaction(VbyteHeap & s) with(s) {
    1153         HandleNode *h;
    1154         char *obase, *nbase, *limit;
    1155        
    1156         NoOfCompactions += 1;
    1157         EndVbyte = StartVbyte;
    1158         h = Header.flink;                                       // ignore header node
    1159         for () {
     1147    HandleNode *h;
     1148    char *obase, *nbase, *limit;
     1149   
     1150    NoOfCompactions += 1;
     1151    EndVbyte = StartVbyte;
     1152    h = Header.flink;                                   // ignore header node
     1153    for () {
    11601154                memmove( EndVbyte, h->s, h->lnth );
    11611155                obase = h->s;
     
    11751169                } // for
    11761170                if ( h == &Header ) break;                      // end of header list ?
    1177         } // for
     1171    } // for
    11781172} // compaction
    11791173
    11801174
    11811175static double heap_expansion_freespace_threshold = 0.1;  // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage)
    1182                                                                                                                 // probably an unreasonable default, but need to assess early-round tests on changing it
     1176                                                        // probably an unreasonable default, but need to assess early-round tests on changing it
    11831177
    11841178void TUNING_set_string_heap_liveness_threshold( double val ) {
    1185         heap_expansion_freespace_threshold = 1.0 - val;
     1179    heap_expansion_freespace_threshold = 1.0 - val;
    11861180}
    11871181
     
    11921186void garbage(VbyteHeap & s, int minreq ) with(s) {
    11931187#ifdef VbyteDebug
    1194         serr | "enter:garbage";
    1195         {
     1188    serr | "enter:garbage";
     1189    {
    11961190                serr | "HandleList:";
    11971191                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    12041198                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    12051199                } // for
    1206         }
    1207 #endif // VbyteDebug
    1208         int AmountUsed, AmountFree;
    1209 
    1210         AmountUsed = 0;
    1211         for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
     1200    }
     1201#endif // VbyteDebug
     1202    int AmountUsed, AmountFree;
     1203
     1204    AmountUsed = 0;
     1205    for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
    12121206                AmountUsed += i->lnth;
    1213         } // for
    1214         AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
    1215        
    1216         if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {       // free space less than threshold or not enough to serve cur request
     1207    } // for
     1208    AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
     1209   
     1210    if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {   // free space less than threshold or not enough to serve cur request
    12171211
    12181212                extend( s, max( CurrSize, minreq ) );                           // extend the heap
    12191213
    1220                 //  Peter says, "This needs work before it should be used."
    1221                 //  } else if ( AmountFree > CurrSize / 2 ) {           // free space greater than 3 times the initial allocation ?
    1222                 //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
    1223 
    1224                 // `extend` implies a `compaction` during the copy
    1225 
    1226         } else {
    1227                 compaction(s);                                  // in-place
    1228         }// if
    1229 #ifdef VbyteDebug
    1230         {
     1214                        //  Peter says, "This needs work before it should be used."
     1215                        //  } else if ( AmountFree > CurrSize / 2 ) {           // free space greater than 3 times the initial allocation ?
     1216                        //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
     1217
     1218        // `extend` implies a `compaction` during the copy
     1219
     1220    } else {
     1221        compaction(s);                                  // in-place
     1222    }// if
     1223#ifdef VbyteDebug
     1224    {
    12311225                serr | "HandleList:";
    12321226                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    12391233                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    12401234                } // for
    1241         }
    1242         serr | "exit:garbage";
     1235    }
     1236    serr | "exit:garbage";
    12431237#endif // VbyteDebug
    12441238} // garbage
     
    12531247void extend( VbyteHeap & s, int size ) with (s) {
    12541248#ifdef VbyteDebug
    1255         serr | "enter:extend, size:" | size;
    1256 #endif // VbyteDebug
    1257         char *OldStartVbyte;
    1258 
    1259         NoOfExtensions += 1;
    1260         OldStartVbyte = StartVbyte;                             // save previous byte area
    1261        
    1262         CurrSize += size > InitSize ? size : InitSize;  // minimum extension, initial size
    1263         StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
    1264         ExtVbyte = (void *)( StartVbyte + CurrSize );
    1265         compaction(s);                                  // copy from old heap to new & adjust pointers to new heap
    1266         free( OldStartVbyte );                          // release old heap
    1267 #ifdef VbyteDebug
    1268         serr | "exit:extend, CurrSize:" | CurrSize;
     1249    serr | "enter:extend, size:" | size;
     1250#endif // VbyteDebug
     1251    char *OldStartVbyte;
     1252
     1253    NoOfExtensions += 1;
     1254    OldStartVbyte = StartVbyte;                         // save previous byte area
     1255   
     1256    CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
     1257    StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     1258    ExtVbyte = (void *)( StartVbyte + CurrSize );
     1259    compaction(s);                                      // copy from old heap to new & adjust pointers to new heap
     1260    free( OldStartVbyte );                              // release old heap
     1261#ifdef VbyteDebug
     1262    serr | "exit:extend, CurrSize:" | CurrSize;
    12691263#endif // VbyteDebug
    12701264} // extend
     
    12781272void VbyteHeap::reduce( int size ) {
    12791273#ifdef VbyteDebug
    1280         serr | "enter:reduce, size:" | size;
    1281 #endif // VbyteDebug
    1282         char *OldStartVbyte;
    1283 
    1284         NoOfReductions += 1;
    1285         OldStartVbyte = StartVbyte;                             // save previous byte area
    1286        
    1287         CurrSize -= size;
    1288         StartVbyte = EndVbyte = new char[CurrSize];
    1289         ExtVbyte = (void *)( StartVbyte + CurrSize );
    1290         compaction();                                   // copy from old heap to new & adjust pointers to new heap
    1291         delete  OldStartVbyte;                          // release old heap
    1292 #ifdef VbyteDebug
    1293         !serr | "exit:reduce, CurrSize:" | CurrSize;
     1274    serr | "enter:reduce, size:" | size;
     1275#endif // VbyteDebug
     1276    char *OldStartVbyte;
     1277
     1278    NoOfReductions += 1;
     1279    OldStartVbyte = StartVbyte;                         // save previous byte area
     1280   
     1281    CurrSize -= size;
     1282    StartVbyte = EndVbyte = new char[CurrSize];
     1283    ExtVbyte = (void *)( StartVbyte + CurrSize );
     1284    compaction();                                       // copy from old heap to new & adjust pointers to new heap
     1285    delete  OldStartVbyte;                              // release old heap
     1286#ifdef VbyteDebug
     1287    serr | "exit:reduce, CurrSize:" | CurrSize;
    12941288#endif // VbyteDebug
    12951289} // reduce
Note: See TracChangeset for help on using the changeset viewer.