Changeset 211def2 for libcfa/src


Ignore:
Timestamp:
Feb 7, 2024, 10:54:16 PM (10 months ago)
Author:
Peter A. Buhr <pabuhr@…>
Branches:
master
Children:
956299b
Parents:
a22d148
Message:

omnibus I/O changes to get quoted manipulator to work

Location:
libcfa/src
Files:
7 edited

Legend:

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

    ra22d148 r211def2  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 14 12:03:47 2024
    13 // Update Count     : 240
     12// Last Modified On : Wed Feb  7 21:17:06 2024
     13// Update Count     : 259
    1414//
    1515
     
    210210}
    211211
    212 void ?|?( ifstream & in, string & s ) {
    213     in | (*s.inner);
    214 }
     212ifstream & ?|?( ifstream & is, _Istream_Squoted f ) {
     213        _Istream_Rquoted f2 = { { f.sstr.s.inner, (_Istream_str_base)f.sstr } };
     214    return is | f2;
     215} // ?|?
    215216
    216217ifstream & ?|?( ifstream & is, _Istream_Sstr f ) {
     218//      _Istream_Rstr f2 = {f.sstr.s.inner, (_Istream_str_base)f.sstr};
    217219        _Istream_Rstr f2 = {f.s.inner, (_Istream_str_base)f};
    218220    return is | f2;
    219221} // ?|?
    220 
    221 void ?|?( ifstream & in, _Istream_Sstr f ) {
    222     (ifstream &)(in | f);
    223 }
    224222
    225223////////////////////////////////////////////////////////
  • libcfa/src/collections/string.hfa

    ra22d148 r211def2  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 14 12:03:46 2024
    13 // Update Count     : 81
     12// Last Modified On : Tue Feb  6 20:59:18 2024
     13// Update Count     : 118
    1414//
    1515
     
    8080void ?|?(ofstream & out, const string & s);
    8181ifstream & ?|?(ifstream & in, string & s);
    82 void ?|?( ifstream & in, string & s );
    8382
    8483static inline {
     
    9695void ?|?( ofstream & os, _Ostream_Manip(string) );
    9796
     97struct _Istream_Swidth {
     98        string & s;
     99        inline _Istream_str_base;
     100}; // _Istream_Swidth
     101
     102struct _Istream_Squoted {
     103        _Istream_Swidth sstr;
     104}; // _Istream_Squoted
     105
    98106struct _Istream_Sstr {
    99107        string & s;
    100108        inline _Istream_str_base;
     109//      _Istream_Swidth sstr;
    101110}; // _Istream_Sstr
    102111
    103112static inline {
    104113        // read width does not include null terminator
    105         _Istream_Sstr wdi( unsigned int rwd, string & s ) { return (_Istream_Sstr)@{ s, {{0p}, rwd, {.flags.rwd : true}} }; }
     114        _Istream_Swidth wdi( unsigned int rwd, string & s ) { return (_Istream_Swidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; }
    106115        _Istream_Sstr getline( string & s, const char delimiter = '\n' ) {
    107                 return (_Istream_Sstr)@{ s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} };
    108         }
    109         _Istream_Sstr & getline( _Istream_Sstr & fmt, const char delimiter = '\n' ) {
    110                 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt;
    111         }
    112         _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ s, {{scanset}, -1, {.flags.inex : false}} }; }
    113         _Istream_Sstr & incl( const char scanset[], _Istream_Sstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }
    114         _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ s, {{scanset}, -1, {.flags.inex : true}} }; }
    115         _Istream_Sstr & excl( const char scanset[], _Istream_Sstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }
    116         _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ s, {{0p}, -1, {.flags.ignore : true}} }; }
    117         _Istream_Sstr & ignore( _Istream_Sstr & fmt ) { fmt.flags.ignore = true; return fmt; }
     116//              return (_Istream_Sstr)@{ { .s : s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } } };
     117                return (_Istream_Sstr)@{ .s : s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } };
     118        }
     119        _Istream_Sstr & getline( _Istream_Swidth & f, const char delimiter = '\n' ) {
     120                f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Sstr &)f;
     121        }
     122        _Istream_Squoted quoted( string & s, const char Ldelimiter = '\"', const char Rdelimiter = '\0' ) {
     123                return (_Istream_Squoted)@{ { .s : s, { {.delimiters : { Ldelimiter, Rdelimiter, '\0' }}, .wd : -1, {.flags.rwd : true} } } };
     124        }
     125        _Istream_Squoted & quoted( _Istream_Swidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) {
     126                f.delimiters[0] = Ldelimiter;  f.delimiters[1] = Rdelimiter;  f.delimiters[2] = '\0';
     127                return (_Istream_Squoted &)f;
     128        }
     129//      _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } } }; }
     130        _Istream_Sstr incl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } }; }
     131        _Istream_Sstr & incl( const char scanset[], _Istream_Swidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Sstr &)f; }
     132//      _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } } }; }
     133        _Istream_Sstr excl( const char scanset[], string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } }; }
     134        _Istream_Sstr & excl( const char scanset[], _Istream_Swidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Sstr &)f; }
     135//      _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ { .s : s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; }
     136        _Istream_Sstr ignore( string & s ) { return (_Istream_Sstr)@{ .s : s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
     137        _Istream_Sstr & ignore( _Istream_Swidth & f ) { f.flags.ignore = true; return (_Istream_Sstr &)f; }
     138        _Istream_Squoted & ignore( _Istream_Squoted & f ) { f.sstr.flags.ignore = true; return (_Istream_Squoted &)f; }
     139//      _Istream_Sstr & ignore( _Istream_Sstr & f ) { f.sstr.flags.ignore = true; return (_Istream_Sstr &)f; }
     140        _Istream_Sstr & ignore( _Istream_Sstr & f ) { f.flags.ignore = true; return (_Istream_Sstr &)f; }
    118141} // distribution
     142ifstream & ?|?( ifstream & is, _Istream_Squoted f );
    119143ifstream & ?|?( ifstream & is, _Istream_Sstr f );
    120 void ?|?( ifstream & is, _Istream_Sstr t );
     144static inline ifstream & ?|?( ifstream & is, _Istream_Swidth f ) { return is | *(_Istream_Sstr *)&f; }
    121145
    122146// Concatenation
  • libcfa/src/collections/string_res.cfa

    ra22d148 r211def2  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Mon Jan 22 23:12:42 2024
    13 // Update Count     : 43
     12// Last Modified On : Wed Feb  7 22:21:33 2024
     13// Update Count     : 81
    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 void ?|?( ifstream & in, string_res & s ) {
    254     (ifstream &)(in | s);
     250        return in;
     251}
     252
     253ifstream & ?|?( ifstream & is, _Istream_Rquoted f ) with( f.rstr ) {
     254        int args;
     255  fini: {
     256                args = fmt( is, "%*[ \f\n\r\t\v]" );                    // remove leading whitespace
     257          if ( eof( is ) ) break fini;
     258                char rfmt[4] = { delimiters[0], '%', 'n', '\0' };
     259                int len = 0;                                                                    // may not be set in fmt
     260                args = fmt( is, rfmt, &len );                                   // remove leading quote
     261          if ( len == 0 || eof( is ) ) break fini;
     262
     263                // Change the remainder of the read into a getline by reseting the closing delimiter.
     264                if ( delimiters[1] != '\0' ) {
     265                        delimiters[0] = delimiters[1];
     266                        delimiters[1] = '\0';
     267                } // if
     268                flags.delimiter = true;
     269                return is | *(_Istream_Rstr *)&f;
     270        } // fini
     271        // read failed => no pattern match => set string to null
     272        if ( ! flags.ignore && s != 0p && args == 0 ) s[0] = '\0';
     273        if ( args == 1 && eof( is ) ) {                                         // data but scan ended at EOF
     274                clear( is );                                                                    // => reset EOF => detect again on next read
     275        } // if
     276        return is;
    255277}
    256278
     
    295317} // ?|?
    296318
    297 void ?|?( ifstream & in, _Istream_Rstr f ) {
    298     (ifstream &)(in | f);
    299 }
    300 
    301 
    302319// Empty constructor
    303320void ?{}(string_res & s) with(s) {
    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 }
     321        if( ambient_string_sharectx->activeHeap ) {
     322                (Handle){ * ambient_string_sharectx->activeHeap };
     323                (shareEditSet_owns_ulink){ false };
     324                verify( Handle.s == 0p && Handle.lnth == 0 );
     325        } else {
     326                (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
     327                (shareEditSet_owns_ulink){ true };
     328                Handle.s = Handle.ulink->StartVbyte;
     329                verify( Handle.lnth == 0 );
     330        }
     331        s.shareEditSet_prev = &s;
     332        s.shareEditSet_next = &s;
     333                }
    317334
    318335static void eagerCopyCtorHelper(string_res & s, const char * rhs, size_t rhslnth) with(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;
     336        if( ambient_string_sharectx->activeHeap ) {
     337                (Handle){ * ambient_string_sharectx->activeHeap };
     338                (shareEditSet_owns_ulink){ false };
     339        } else {
     340                (Handle){ * new( rhslnth ) };
     341                (shareEditSet_owns_ulink){ true };
     342        }
     343        Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     344        Handle.lnth = rhslnth;
     345        memmove( Handle.s, rhs, rhslnth );
     346        s.shareEditSet_prev = &s;
     347        s.shareEditSet_next = &s;
    331348}
    332349
    333350// Constructor from a raw buffer and size
    334351void ?{}(string_res & s, const char * rhs, size_t rhslnth) with(s) {
    335     eagerCopyCtorHelper(s, rhs, rhslnth);
     352        eagerCopyCtorHelper(s, rhs, rhslnth);
    336353}
    337354
    338355void ?{}( string_res & s, ssize_t rhs ) {
    339     char buf[64];
    340     int len;
    341     snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
    342     ( s ){ buf, len };
     356        char buf[64];
     357        int len;
     358        snprintf( buf, sizeof(buf)-1, "%zd%n", rhs, &len );
     359        ( s ){ buf, len };
    343360}
    344361void ?{}( string_res & s, size_t rhs ) {
    345     char buf[64];
    346     int len;
    347     snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
    348     ( s ){ buf, len };
     362        char buf[64];
     363        int len;
     364        snprintf( buf, sizeof(buf)-1, "%zu%n", rhs, &len );
     365        ( s ){ buf, len };
    349366}
    350367void ?{}( string_res & s, double rhs ) {
    351     char buf[64];
    352     int len;
    353     snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
    354     ( s ){ buf, len };
     368        char buf[64];
     369        int len;
     370        snprintf( buf, sizeof(buf)-1, "%g%n", rhs, &len );
     371        ( s ){ buf, len };
    355372}
    356373void ?{}( string_res & s, long double rhs ) {
    357     char buf[64];
    358     int len;
    359     snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
    360     ( s ){ buf, len };
     374        char buf[64];
     375        int len;
     376        snprintf( buf, sizeof(buf)-1, "%Lg%n", rhs, &len );
     377        ( s ){ buf, len };
    361378}
    362379void ?{}( string_res & s, double _Complex rhs ) {
    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 };
     380        char buf[64];
     381        int len;
     382        snprintf( buf, sizeof(buf)-1, "%g+%gi%n", creal( rhs ), cimag( rhs ), &len );
     383        ( s ){ buf, len };
    367384}
    368385void ?{}( string_res & s, long double _Complex rhs ) {
    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 };
     386        char buf[64];
     387        int len;
     388        snprintf( buf, sizeof(buf)-1, "%Lg+%Lgi%n", creall( rhs ), cimagl( rhs ), &len );
     389        ( s ){ buf, len };
    373390}
    374391
    375392// private ctor (not in header): use specified heap (ignore ambient) and copy chars in
    376393void ?{}( string_res & s, VbyteHeap & heap, const char * rhs, size_t rhslnth ) with(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;
     394        (Handle){ heap };
     395        Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
     396        Handle.lnth = rhslnth;
     397        (s.shareEditSet_owns_ulink){ false };
     398        memmove( Handle.s, rhs, rhslnth );
     399        s.shareEditSet_prev = &s;
     400        s.shareEditSet_next = &s;
    384401}
    385402
     
    387404// General copy constructor
    388405void ?{}(string_res & s, const string_res & s2, StrResInitMode mode, size_t start, size_t len ) {
    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     }
     406        size_t end = start + len;
     407        verify( start <= end && end <= s2.Handle.lnth );
     408
     409        if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) {
     410                // crossing heaps (including private): copy eagerly
     411                eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
     412                verify(s.shareEditSet_prev == &s);
     413                verify(s.shareEditSet_next == &s);
     414        } else {
     415                (s.Handle){};
     416                s.Handle.s = s2.Handle.s + start;
     417                s.Handle.lnth = end - start;
     418                s.Handle.ulink = s2.Handle.ulink;
     419
     420                AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
     421                // ^ bug?  skip others at early point in string
     422
     423                if (mode == COPY_VALUE) {
     424                        verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap);
     425                        // requested logical copy in same heap: defer copy until write
     426
     427                        (s.shareEditSet_owns_ulink){ false };
     428
     429                        // make s alone in its shareEditSet
     430                        s.shareEditSet_prev = &s;
     431                        s.shareEditSet_next = &s;
     432                } else {
     433                        verify( mode == SHARE_EDITS );
     434                        // sharing edits with source forces same heap as source (ignore context)
     435
     436                        (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink };
     437
     438                        // s2 is logically const but not implementation const
     439                        string_res & s2mod = (string_res &) s2;
     440
     441                        // insert s after s2 on shareEditSet
     442                        s.shareEditSet_next = s2mod.shareEditSet_next;
     443                        s.shareEditSet_prev = &s2mod;
     444                        s.shareEditSet_next->shareEditSet_prev = &s;
     445                        s.shareEditSet_prev->shareEditSet_next = &s;
     446                }
     447        }
    432448}
    433449
    434450static void assignEditSet(string_res & s, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
    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     }
     451                                                  char * resultSesStart,
     452                                                  size_t resultSesLnth,
     453                                                  HandleNode * resultPadPosition, size_t bsize ) {
     454
     455        char * beforeBegin = shareEditSetStartPeer->Handle.s;
     456        size_t beforeLen = s.Handle.s - beforeBegin;
     457
     458        char * afterBegin = s.Handle.s + s.Handle.lnth;
     459        size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
     460
     461        size_t oldLnth = s.Handle.lnth;
     462
     463        s.Handle.s = resultSesStart + beforeLen;
     464        s.Handle.lnth = bsize;
     465        if (resultPadPosition)
     466                MoveThisAfter( s.Handle, *resultPadPosition );
     467
     468        // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
     469        char *limit = resultSesStart + resultSesLnth;
     470        for ( string_res * p = s.shareEditSet_next; p != &s; p = p->shareEditSet_next ) {
     471                verify (p->Handle.s >= beforeBegin);
     472                if ( p->Handle.s >= afterBegin ) {
     473                        verify ( p->Handle.s <= afterBegin + afterLen );
     474                        verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     475                        // p starts after the edit
     476                        // take start and end as end-anchored
     477                        size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
     478                        p->Handle.s = limit - startOffsetFromEnd;
     479                        // p->Handle.lnth unaffected
     480                } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
     481                        // p starts before, or at the start of, the edit
     482                        if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
     483                                // p ends before the edit
     484                                // take end as start-anchored too
     485                                // p->Handle.lnth unaffected
     486                        } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
     487                                // p ends during the edit; p does not include the last character replaced
     488                                // clip end of p to end at start of edit
     489                                p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
     490                        } else {
     491                                // p ends after the edit
     492                                verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
     493                                // take end as end-anchored
     494                                // stretch-shrink p according to the edit
     495                                p->Handle.lnth += s.Handle.lnth;
     496                                p->Handle.lnth -= oldLnth;
     497                        }
     498                        // take start as start-anchored
     499                        size_t startOffsetFromStart = p->Handle.s - beforeBegin;
     500                        p->Handle.s = resultSesStart + startOffsetFromStart;
     501                } else {
     502                        verify ( p->Handle.s < afterBegin );
     503                        // p starts during the edit
     504                        verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
     505                        if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
     506                                // p ends during the edit; p does not include the last character replaced
     507                                // set p to empty string at start of edit
     508                                p->Handle.s = s.Handle.s;
     509                                p->Handle.lnth = 0;
     510                        } else {
     511                                // p includes the end of the edit
     512                                // clip start of p to start at end of edit
     513                                int charsToClip = afterBegin - p->Handle.s;
     514                                p->Handle.s = s.Handle.s + s.Handle.lnth;
     515                                p->Handle.lnth -= charsToClip;
     516                        }
     517                }
     518                if (resultPadPosition)
     519                        MoveThisAfter( p->Handle, *resultPadPosition ); // move substring handle to maintain sorted order by string position
     520        }
    505521}
    506522
    507523// traverse the share-edit set (SES) to recover the range of a base string to which `s` belongs
    508524static void locateInShareEditSet( string_res & s, string_res *& shareEditSetStartPeer, string_res *& shareEditSetEndPeer ) {
    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     }
     525        shareEditSetStartPeer = & s;
     526        shareEditSetEndPeer = & s;
     527        for (string_res * editPeer = s.shareEditSet_next; editPeer != &s; editPeer = editPeer->shareEditSet_next) {
     528                if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
     529                        shareEditSetStartPeer = editPeer;
     530                }
     531                if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
     532                        shareEditSetEndPeer = editPeer;
     533                }
     534        }
    519535}
    520536
    521537static string_res & assign_(string_res & s, const char * buffer, size_t bsize, const string_res & valSrc) {
    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;
     538        string_res * shareEditSetStartPeer;
     539        string_res * shareEditSetEndPeer;
     540        locateInShareEditSet( s, shareEditSetStartPeer, shareEditSetEndPeer );
     541
     542        verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
     543        size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
     544        verify( origEditSetLength >= s.Handle.lnth );
     545
     546        if ( s.shareEditSet_owns_ulink ) {                               // assigning to private context
     547                // ok to overwrite old value within LHS
     548                char * prefixStartOrig = shareEditSetStartPeer->Handle.s;
     549                int prefixLen = s.Handle.s - prefixStartOrig;
     550                char * suffixStartOrig = s.Handle.s + s.Handle.lnth;
     551                int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig;
     552
     553                int delta = bsize - s.Handle.lnth;
     554                if ( char * oldBytes = VbyteTryAdjustLast( *s.Handle.ulink, delta ) ) {
     555                        // growing: copy from old to new
     556                        char * dest = VbyteAlloc( *s.Handle.ulink, origEditSetLength + delta );
     557                        char *destCursor = dest;  memcpy(destCursor, prefixStartOrig, prefixLen);
     558                        destCursor += prefixLen;  memcpy(destCursor, buffer              , bsize        );
     559                        destCursor += bsize;      memcpy(destCursor, suffixStartOrig, suffixLen);
     560                        assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     561                                                  dest,
     562                                                  origEditSetLength + delta,
     563                                                  0p, bsize);
     564                        free( oldBytes );
     565                } else {
     566                        // room is already allocated in-place: bubble suffix and overwite middle
     567                        memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen );
     568                        memcpy( s.Handle.s, buffer, bsize );
     569
     570                        assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     571                                                  shareEditSetStartPeer->Handle.s,
     572                                                  origEditSetLength + delta,
     573                                                  0p, bsize);
     574                }
     575
     576        } else if (                                                                                // assigning to shared context
     577                s.Handle.lnth == origEditSetLength &&             // overwriting entire run of SES
     578                & valSrc &&                                                                        // sourcing from a managed string
     579                valSrc.Handle.ulink == s.Handle.ulink  ) {       // sourcing from same heap
     580
     581                // SES's result will only use characters from the source string => reuse source
     582                assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     583                                          valSrc.Handle.s,
     584                                          valSrc.Handle.lnth,
     585                                          &((string_res&)valSrc).Handle, bsize);
     586               
     587        } else {
     588                // overwriting a proper substring of some string: mash characters from old and new together (copy on write)
     589                // OR we are importing characters: need to copy eagerly (can't refer to source)
     590
     591                // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
     592                // `s` occurs in the middle of it, to be replaced
     593                // build up the new text in `pasting`
     594
     595                string_res pasting = {
     596                        * s.Handle.ulink,                                                          // maintain same heap, regardless of context
     597                        shareEditSetStartPeer->Handle.s,                                   // start of SES
     598                        s.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before s
     599                append( pasting,
     600                                buffer,                                                                                 // start of replacement for s
     601                                bsize );                                                                                   // length of replacement for s
     602                append( pasting,
     603                                s.Handle.s + s.Handle.lnth,                               // start of SES after s
     604                                shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
     605                                (s.Handle.s + s.Handle.lnth) );                   // length of SES, after s
     606
     607                // The above string building can trigger compaction.
     608                // The reference points (that are arguments of the string building) may move during that building.
     609                // From s point on, they are stable.
     610
     611                assignEditSet(s, shareEditSetStartPeer, shareEditSetEndPeer,
     612                                          pasting.Handle.s,
     613                                          pasting.Handle.lnth,
     614                                          &pasting.Handle, bsize);
     615        }
     616
     617        return s;
    603618}
    604619
    605620string_res & assign(string_res & s, const string_res & src, size_t maxlen) {
    606     return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
     621        return assign_(s, src.Handle.s, min(src.Handle.lnth, maxlen), *0p);
    607622}
    608623
    609624string_res & assign(string_res & s, const char * buffer, size_t bsize) {
    610     return assign_(s, buffer, bsize, *0p);
     625        return assign_(s, buffer, bsize, *0p);
    611626}
    612627
    613628string_res & ?=?(string_res & s, char c) {
    614     return assign(s, &c, 1);
     629        return assign(s, &c, 1);
    615630}
    616631
    617632string_res & ?=?( string_res & s, ssize_t rhs ) {
    618     string_res rhs2 = rhs;
    619     s = rhs2;
    620     return s;
     633        string_res rhs2 = rhs;
     634        s = rhs2;
     635        return s;
    621636}
    622637string_res & ?=?( string_res & s, size_t rhs ) {
    623     string_res rhs2 = rhs;
    624     s = rhs2;
    625     return s;
     638        string_res rhs2 = rhs;
     639        s = rhs2;
     640        return s;
    626641}
    627642string_res & ?=?( string_res & s, double rhs ) {
    628     string_res rhs2 = rhs;
    629     s = rhs2;
    630     return s;
     643        string_res rhs2 = rhs;
     644        s = rhs2;
     645        return s;
    631646}
    632647string_res & ?=?( string_res & s, long double rhs ) {
    633     string_res rhs2 = rhs;
    634     s = rhs2;
    635     return s;
     648        string_res rhs2 = rhs;
     649        s = rhs2;
     650        return s;
    636651}
    637652string_res & ?=?( string_res & s, double _Complex rhs ) {
    638     string_res rhs2 = rhs;
    639     s = rhs2;
    640     return s;
     653        string_res rhs2 = rhs;
     654        s = rhs2;
     655        return s;
    641656}
    642657string_res & ?=?( string_res & s, long double _Complex rhs ) {
    643     string_res rhs2 = rhs;
    644     s = rhs2;
    645     return s;
     658        string_res rhs2 = rhs;
     659        s = rhs2;
     660        return s;
    646661}
    647662
    648663// Copy assignment operator
    649664string_res & ?=?(string_res & s, const string_res & rhs) with( s ) {
    650     return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
     665        return assign_(s, rhs.Handle.s, rhs.Handle.lnth, rhs);
    651666}
    652667
    653668string_res & ?=?(string_res & s, string_res & rhs) with( s ) {
    654     const string_res & rhs2 = rhs;
    655     return s = rhs2;
     669        const string_res & rhs2 = rhs;
     670        return s = rhs2;
    656671}
    657672
     
    659674// Destructor
    660675void ^?{}(string_res & s) with(s) {
    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     }
     676        // much delegated to implied ^VbyteSM
     677
     678        // sever s from its share-edit peers, if any (four no-ops when already solo)
     679        s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
     680        s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
     681        // s.shareEditSet_next = &s;
     682        // s.shareEditSet_prev = &s;
     683
     684        if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
     685                delete( s.Handle.ulink );
     686        }
    672687}
    673688
     
    677692// offset from the start of the string.
    678693char ?[?](const string_res & s, size_t index) with(s) {
    679     //TODO: Check if index is valid (no exceptions yet)
    680     return Handle.s[index];
     694        //TODO: Check if index is valid (no exceptions yet)
     695        return Handle.s[index];
    681696}
    682697
    683698void assignAt(const string_res & s, size_t index, char val) {
    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);
     699        // caution: not tested (not reachable by string-api-coverage interface)
     700        // equivalent form at string level is `s[index] = val`,
     701        // which uses the overload that returns a length-1 string
     702        string_res editZone = { s, SHARE_EDITS, index, 1 };
     703        assign(editZone, &val, 1);
    689704}
    690705
     
    694709
    695710void append(string_res & str1, const char * buffer, size_t bsize) {
    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;
     711        size_t clnth = str1.Handle.lnth + bsize;
     712        if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ?
     713                // no-op
     714        } else {                                                                                        // must copy some text
     715                if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
     716                        VbyteAlloc( *str1.Handle.ulink, bsize );        // create room for 2nd part at the end of string area
     717                } else {                                                                                // copy the two parts
     718                        char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth );
     719                        char * str1oldBuf = str1.Handle.s;                      // must read after VbyteAlloc call in case it gs's
     720                        str1.Handle.s = str1newBuf;
     721                        memcpy( str1.Handle.s, str1oldBuf,  str1.Handle.lnth );
     722                } // if
     723                memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize );
     724        } // if
     725        str1.Handle.lnth = clnth;
    711726}
    712727
    713728void ?+=?(string_res & str1, const string_res & str2) {
    714     append( str1, str2.Handle.s, str2.Handle.lnth );
     729        append( str1, str2.Handle.s, str2.Handle.lnth );
    715730}
    716731
    717732void append(string_res & str1, const string_res & str2, size_t maxlen) {
    718     append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
     733        append( str1, str2.Handle.s, min(str2.Handle.lnth, maxlen) );
    719734}
    720735
    721736void ?+=?(string_res & s, char c) {
    722     append( s, & c, 1 );
     737        append( s, & c, 1 );
    723738}
    724739void ?+=?(string_res & s, const char * c) {
    725     append( s, c, strlen(c) );
     740        append( s, c, strlen(c) );
    726741}
    727742
     
    730745
    731746void ?*=?(string_res & s, size_t factor) {
    732     string_res s2 = { s, COPY_VALUE };
    733     s = "";
    734     for (factor) s += s2;
     747        string_res s2 = { s, COPY_VALUE };
     748        s = "";
     749        for (factor) s += s2;
    735750}
    736751
     
    739754
    740755int strcmp(const string_res & s1, const string_res & s2) {
    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;
     756        // return 0;
     757        int ans1 = memcmp(s1.Handle.s, s2.Handle.s, min(s1.Handle.lnth, s2.Handle.lnth));
     758        if (ans1 != 0) return ans1;
     759        return s1.Handle.lnth - s2.Handle.lnth;
    745760}
    746761
     
    753768
    754769int strcmp (const string_res & s1, const char * s2) {
    755     string_res s2x = s2;
    756     return strcmp(s1, s2x);
     770        string_res s2x = s2;
     771        return strcmp(s1, s2x);
    757772}
    758773
     
    765780
    766781int strcmp (const char * s1, const string_res & s2) {
    767     string_res s1x = s1;
    768     return strcmp(s1x, s2);
     782        string_res s1x = s1;
     783        return strcmp(s1x, s2);
    769784}
    770785
     
    777792
    778793
    779 
    780794//////////////////////////////////////////////////////////
    781795// Search
    782796
    783797bool contains(const string_res & s, char ch) {
    784     for ( i; size(s) ) {
    785         if (s[i] == ch) return true;
    786     }
    787     return false;
     798        for ( i; size(s) ) {
     799                if (s[i] == ch) return true;
     800        }
     801        return false;
    788802}
    789803
    790804int find(const string_res & s, char search) {
    791     return findFrom(s, 0, search);
     805        return findFrom(s, 0, search);
    792806}
    793807
    794808int findFrom(const string_res & s, size_t fromPos, char search) {
    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;
     809        // FIXME: This paricular overload (find of single char) is optimized to use memchr.
     810        // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match.
     811        char * searchFrom = s.Handle.s + fromPos;
     812        size_t searchLnth = s.Handle.lnth - fromPos;
     813        int searchVal = search;
     814        char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth);
     815        if (foundAt == 0p) return s.Handle.lnth;
     816        else return foundAt - s.Handle.s;
    803817}
    804818
    805819int find(const string_res & s, const string_res & search) {
    806     return findFrom(s, 0, search);
     820        return findFrom(s, 0, search);
    807821}
    808822
    809823int findFrom(const string_res & s, size_t fromPos, const string_res & search) {
    810     return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
     824        return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth);
    811825}
    812826
    813827int find(const string_res & s, const char * search) {
    814     return findFrom(s, 0, search);
     828        return findFrom(s, 0, search);
    815829}
    816830int findFrom(const string_res & s, size_t fromPos, const char * search) {
    817     return findFrom(s, fromPos, search, strlen(search));
     831        return findFrom(s, fromPos, search, strlen(search));
    818832}
    819833
    820834int find(const string_res & s, const char * search, size_t searchsize) {
    821     return findFrom(s, 0, search, searchsize);
     835        return findFrom(s, 0, search, searchsize);
    822836}
    823837
    824838int findFrom(const string_res & s, size_t fromPos, const char * search, size_t searchsize) {
    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;
     839        /* Remaining implementations essentially ported from Sunjay's work */
     840
     841        // FIXME: This is a naive algorithm. We probably want to switch to someting
     842        // like Boyer-Moore in the future.
     843        // https://en.wikipedia.org/wiki/String_searching_algorithm
     844
     845        // Always find the empty string
     846        if (searchsize == 0) {
     847                return 0;
     848        }
     849
     850        for ( i; fromPos ~ s.Handle.lnth ) {
     851                size_t remaining = s.Handle.lnth - i;
     852                // Never going to find the search string if the remaining string is
     853                // smaller than search
     854                if (remaining < searchsize) {
     855                        break;
     856                }
     857
     858                bool matched = true;
     859                for ( j; searchsize ) {
     860                        if (search[j] != s.Handle.s[i + j]) {
     861                                matched = false;
     862                                break;
     863                        }
     864                }
     865                if (matched) {
     866                        return i;
     867                }
     868        }
     869        return s.Handle.lnth;
    859870}
    860871
    861872bool includes(const string_res & s, const string_res & search) {
    862     return includes(s, search.Handle.s, search.Handle.lnth);
     873        return includes(s, search.Handle.s, search.Handle.lnth);
    863874}
    864875
    865876bool includes(const string_res & s, const char * search) {
    866     return includes(s, search, strlen(search));
     877        return includes(s, search, strlen(search));
    867878}
    868879
    869880bool includes(const string_res & s, const char * search, size_t searchsize) {
    870     return find(s, search, searchsize) < s.Handle.lnth;
     881        return find(s, search, searchsize) < s.Handle.lnth;
    871882}
    872883
    873884bool startsWith(const string_res & s, const string_res & prefix) {
    874     return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
     885        return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
    875886}
    876887
    877888bool startsWith(const string_res & s, const char * prefix) {
    878     return startsWith(s, prefix, strlen(prefix));
     889        return startsWith(s, prefix, strlen(prefix));
    879890}
    880891
    881892bool startsWith(const string_res & s, const char * prefix, size_t prefixsize) {
    882     if (s.Handle.lnth < prefixsize) {
    883         return false;
    884     }
    885     return memcmp(s.Handle.s, prefix, prefixsize) == 0;
     893        if (s.Handle.lnth < prefixsize) {
     894                return false;
     895        }
     896        return memcmp(s.Handle.s, prefix, prefixsize) == 0;
    886897}
    887898
    888899bool endsWith(const string_res & s, const string_res & suffix) {
    889     return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
     900        return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
    890901}
    891902
    892903bool endsWith(const string_res & s, const char * suffix) {
    893     return endsWith(s, suffix, strlen(suffix));
     904        return endsWith(s, suffix, strlen(suffix));
    894905}
    895906
    896907bool endsWith(const string_res & s, const char * suffix, size_t suffixsize) {
    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 
     908        if (s.Handle.lnth < suffixsize) {
     909                return false;
     910        }
     911        // Amount to offset the bytes pointer so that we are comparing the end of s
     912        // to suffix. s.bytes + offset should be the first byte to compare against suffix
     913        size_t offset = s.Handle.lnth - suffixsize;
     914        return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
     915}
     916
     917/* Back to Mike's work */
    908918
    909919///////////////////////////////////////////////////////////////////////////
     
    911921
    912922void ?{}( charclass_res & s, const string_res & chars) {
    913     (s){ chars.Handle.s, chars.Handle.lnth };
     923        (s){ chars.Handle.s, chars.Handle.lnth };
    914924}
    915925
    916926void ?{}( charclass_res & s, const char * chars ) {
    917     (s){ chars, strlen(chars) };
     927        (s){ chars, strlen(chars) };
    918928}
    919929
    920930void ?{}( charclass_res & s, const char * chars, size_t charssize ) {
    921     (s.chars){ chars, charssize };
    922     // now sort it ?
     931        (s.chars){ chars, charssize };
     932        // now sort it ?
    923933}
    924934
    925935void ^?{}( charclass_res & s ) {
    926     ^(s.chars){};
     936        ^(s.chars){};
    927937}
    928938
    929939static bool test( const charclass_res & mask, char c ) {
    930     // instead, use sorted char list?
    931     return contains( mask.chars, c );
     940        // instead, use sorted char list?
     941        return contains( mask.chars, c );
    932942}
    933943
    934944int exclude(const string_res & s, const charclass_res & mask) {
    935     for ( i; size(s) ) {
    936         if ( test(mask, s[i]) ) return i;
    937     }
    938     return size(s);
     945        for ( i; size(s) ) {
     946                if ( test(mask, s[i]) ) return i;
     947        }
     948        return size(s);
    939949}
    940950
    941951int include(const string_res & s, const charclass_res & mask) {
    942     for ( i; size(s) ) {
    943         if ( ! test(mask, s[i]) ) return i;
    944     }
    945     return size(s);
     952        for ( i; size(s) ) {
     953                if ( ! test(mask, s[i]) ) return i;
     954        }
     955        return size(s);
    946956}
    947957
     
    953963static void AddThisAfter( HandleNode & s, HandleNode & n ) with(s) {
    954964#ifdef VbyteDebug
    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     {
     965        serr | "enter:AddThisAfter, s:" | &s | " n:" | &n;
     966#endif // VbyteDebug
     967        // 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).
     968        verify( n.ulink != 0p );
     969        verify( s.ulink == n.ulink );
     970        flink = n.flink;
     971        blink = &n;
     972        n.flink->blink = &s;
     973        n.flink = &s;
     974#ifdef VbyteDebug
     975        {
    966976                serr | "HandleList:";
    967977                serr | nlOff;
     
    974984                } // for
    975985                serr | nlOn;
    976     }
    977     serr | "exit:AddThisAfter";
     986        }
     987        serr | "exit:AddThisAfter";
    978988#endif // VbyteDebug
    979989} // AddThisAfter
     
    984994static void DeleteNode( HandleNode & s ) with(s) {
    985995#ifdef VbyteDebug
    986     serr | "enter:DeleteNode, s:" | &s;
    987 #endif // VbyteDebug
    988     flink->blink = blink;
    989     blink->flink = flink;
    990 #ifdef VbyteDebug
    991     serr | "exit:DeleteNode";
     996        serr | "enter:DeleteNode, s:" | &s;
     997#endif // VbyteDebug
     998        flink->blink = blink;
     999        blink->flink = flink;
     1000#ifdef VbyteDebug
     1001        serr | "exit:DeleteNode";
    9921002#endif // VbyteDebug
    9931003} //  DeleteNode
     
    9991009static char * VbyteAlloc( VbyteHeap & s, int size ) with(s) {
    10001010#ifdef VbyteDebug
    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 ?
     1011        serr | "enter:VbyteAlloc, size:" | size;
     1012#endif // VbyteDebug
     1013        uintptr_t NoBytes;
     1014        char *r;
     1015
     1016        NoBytes = ( uintptr_t )EndVbyte + size;
     1017        if ( NoBytes > ( uintptr_t )ExtVbyte ) {                        // enough room for new byte-string ?
    10081018                garbage( s, size );                                                             // firer up the garbage collector
    10091019                verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte  && "garbage run did not free up required space" );
    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;
     1020        } // if
     1021        r = EndVbyte;
     1022        EndVbyte += size;
     1023#ifdef VbyteDebug
     1024        serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
     1025#endif // VbyteDebug
     1026        return r;
    10171027} // VbyteAlloc
    10181028
     
    10271037
    10281038static char * VbyteTryAdjustLast( VbyteHeap & s, int delta ) with(s) {
    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;
     1039        if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) {
     1040                // room available
     1041                EndVbyte += delta;
     1042                return 0p;
     1043        }
     1044
     1045        char *oldBytes = StartVbyte;
     1046
     1047        NoOfExtensions += 1;
     1048        CurrSize *= 2;
     1049        StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     1050        ExtVbyte = StartVbyte + CurrSize;
     1051
     1052        return oldBytes;
    10431053}
    10441054
     
    10491059static void MoveThisAfter( HandleNode & s, const HandleNode  & h ) with(s) {
    10501060#ifdef VbyteDebug
    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
     1061        serr | "enter:MoveThisAfter, s:" | & s | " h:" | & h;
     1062#endif // VbyteDebug
     1063        verify( h.ulink != 0p );
     1064        verify( s.ulink == h.ulink );
     1065        if ( s < h.s ) {                                        // check argument values
    10561066                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
    1057                 //      | ( h->s ) | " and keep handles in ascending order";
     1067                //        | ( h->s ) | " and keep handles in ascending order";
    10581068                // exit(-1 );
    10591069                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
    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 ) {
     1070        } // if
     1071
     1072        HandleNode *i;
     1073        for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
     1074        if ( & s != i->blink ) {
    10651075                DeleteNode( s );
    10661076                AddThisAfter( s, *i->blink );
    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";
     1077        } // if
     1078#ifdef VbyteDebug
     1079        {
     1080                serr | "HandleList:";
     1081                serr | nlOff;
     1082                for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
     1083                        serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
     1084                        for ( i; n->lnth ) {
     1085                                serr | n->s[i];
     1086                        } // for
     1087                        serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
     1088                } // for
     1089                serr | nlOn;
     1090        }
     1091        serr | "exit:MoveThisAfter";
    10821092#endif // VbyteDebug
    10831093} // MoveThisAfter
    1084 
    1085 
    1086 
    10871094
    10881095
     
    10971104int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  {
    10981105#ifdef VbyteDebug
    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        serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
     1107#endif // VbyteDebug
     1108        int cmp;
     1109
     1110  CharZip: for ( int i = 0; ; i += 1 ) {
     1111                if ( i == Src2Lnth - 1 ) {
     1112                        for ( ; ; i += 1 ) {
     1113                                if ( i == Src1Lnth - 1 ) {
     1114                                        cmp = 0;
     1115                                        break CharZip;
     1116                                } // exit
     1117                                if ( Src1[Src1Start + i] != ' ') {
     1118                                        // SUSPECTED BUG:  this could be be why Peter got the bug report about == " "  (why is this case here at all?)
     1119                                        cmp = 1;
     1120                                        break CharZip;
     1121                                } // exit
     1122                        } // for
     1123                } // exit
    11061124                if ( i == Src1Lnth - 1 ) {
    1107                     cmp = 0;
    1108                     break CharZip;
     1125                        for ( ; ; i += 1 ) {
     1126                                if ( i == Src2Lnth - 1 ) {
     1127                                        cmp = 0;
     1128                                        break CharZip;
     1129                                } // exit
     1130                                if ( Src2[Src2Start + i] != ' ') {
     1131                                        cmp = -1;
     1132                                        break CharZip;
     1133                                } // exit
     1134                        } // for
    11091135                } // exit
    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;
     1136                if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
     1137                        cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
     1138                        break CharZip;
    11141139                } // exit
    1115             } // for
    1116         } // exit
    1117         if ( i == Src1Lnth - 1 ) {
    1118             for ( ; ; i += 1 ) {
    1119                 if ( i == Src2Lnth - 1 ) {
    1120                     cmp = 0;
    1121                     break CharZip;
    1122                 } // exit
    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;
     1140        } // for
     1141#ifdef VbyteDebug
     1142        serr | "exit:ByteCmp, cmp:" | cmp;
     1143#endif // VbyteDebug
     1144        return cmp;
    11381145} // ByteCmp
    11391146
     
    11451152
    11461153void compaction(VbyteHeap & s) with(s) {
    1147     HandleNode *h;
    1148     char *obase, *nbase, *limit;
    1149    
    1150     NoOfCompactions += 1;
    1151     EndVbyte = StartVbyte;
    1152     h = Header.flink;                                   // ignore header node
    1153     for () {
     1154        HandleNode *h;
     1155        char *obase, *nbase, *limit;
     1156       
     1157        NoOfCompactions += 1;
     1158        EndVbyte = StartVbyte;
     1159        h = Header.flink;                                       // ignore header node
     1160        for () {
    11541161                memmove( EndVbyte, h->s, h->lnth );
    11551162                obase = h->s;
     
    11691176                } // for
    11701177                if ( h == &Header ) break;                      // end of header list ?
    1171     } // for
     1178        } // for
    11721179} // compaction
    11731180
    11741181
    11751182static double heap_expansion_freespace_threshold = 0.1;  // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage)
    1176                                                         // probably an unreasonable default, but need to assess early-round tests on changing it
     1183                                                                                                                // probably an unreasonable default, but need to assess early-round tests on changing it
    11771184
    11781185void TUNING_set_string_heap_liveness_threshold( double val ) {
    1179     heap_expansion_freespace_threshold = 1.0 - val;
     1186        heap_expansion_freespace_threshold = 1.0 - val;
    11801187}
    11811188
     
    11861193void garbage(VbyteHeap & s, int minreq ) with(s) {
    11871194#ifdef VbyteDebug
    1188     serr | "enter:garbage";
    1189     {
     1195        serr | "enter:garbage";
     1196        {
    11901197                serr | "HandleList:";
    11911198                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    11981205                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    11991206                } // for
    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
     1207        }
     1208#endif // VbyteDebug
     1209        int AmountUsed, AmountFree;
     1210
     1211        AmountUsed = 0;
     1212        for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
    12061213                AmountUsed += i->lnth;
    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
     1214        } // for
     1215        AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
     1216       
     1217        if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) {       // free space less than threshold or not enough to serve cur request
    12111218
    12121219                extend( s, max( CurrSize, minreq ) );                           // extend the heap
    12131220
    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     {
     1221                //  Peter says, "This needs work before it should be used."
     1222                //  } else if ( AmountFree > CurrSize / 2 ) {           // free space greater than 3 times the initial allocation ?
     1223                //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
     1224
     1225                // `extend` implies a `compaction` during the copy
     1226
     1227        } else {
     1228                compaction(s);                                  // in-place
     1229        }// if
     1230#ifdef VbyteDebug
     1231        {
    12251232                serr | "HandleList:";
    12261233                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
     
    12331240                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
    12341241                } // for
    1235     }
    1236     serr | "exit:garbage";
     1242        }
     1243        serr | "exit:garbage";
    12371244#endif // VbyteDebug
    12381245} // garbage
     
    12471254void extend( VbyteHeap & s, int size ) with (s) {
    12481255#ifdef VbyteDebug
    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;
     1256        serr | "enter:extend, size:" | size;
     1257#endif // VbyteDebug
     1258        char *OldStartVbyte;
     1259
     1260        NoOfExtensions += 1;
     1261        OldStartVbyte = StartVbyte;                             // save previous byte area
     1262       
     1263        CurrSize += size > InitSize ? size : InitSize;  // minimum extension, initial size
     1264        StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize);
     1265        ExtVbyte = (void *)( StartVbyte + CurrSize );
     1266        compaction(s);                                  // copy from old heap to new & adjust pointers to new heap
     1267        free( OldStartVbyte );                          // release old heap
     1268#ifdef VbyteDebug
     1269        serr | "exit:extend, CurrSize:" | CurrSize;
    12631270#endif // VbyteDebug
    12641271} // extend
     
    12721279void VbyteHeap::reduce( int size ) {
    12731280#ifdef VbyteDebug
    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;
     1281        serr | "enter:reduce, size:" | size;
     1282#endif // VbyteDebug
     1283        char *OldStartVbyte;
     1284
     1285        NoOfReductions += 1;
     1286        OldStartVbyte = StartVbyte;                             // save previous byte area
     1287       
     1288        CurrSize -= size;
     1289        StartVbyte = EndVbyte = new char[CurrSize];
     1290        ExtVbyte = (void *)( StartVbyte + CurrSize );
     1291        compaction();                                   // copy from old heap to new & adjust pointers to new heap
     1292        delete  OldStartVbyte;                          // release old heap
     1293#ifdef VbyteDebug
     1294        !serr | "exit:reduce, CurrSize:" | CurrSize;
    12881295#endif // VbyteDebug
    12891296} // reduce
  • libcfa/src/collections/string_res.hfa

    ra22d148 r211def2  
    1010// Created On       : Fri Sep 03 11:00:00 2021
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Thu Jan  4 11:28:06 2024
    13 // Update Count     : 27
     12// Last Modified On : Wed Feb  7 21:24:40 2024
     13// Update Count     : 59
    1414//
    1515
     
    123123void ?|?(ofstream & out, const string_res & s);
    124124ifstream & ?|?(ifstream & in, string_res & s);
    125 void ?|?( ifstream & in, string_res & s );
     125
     126struct _Istream_Rwidth {
     127        string_res * s;
     128        inline _Istream_str_base;
     129}; // _Istream_Rwidth
     130
     131struct _Istream_Rquoted {
     132        // string_res * s;
     133        // inline _Istream_str_base;
     134        _Istream_Rwidth rstr;
     135}; // _Istream_Rquoted
    126136
    127137struct _Istream_Rstr {
    128138        string_res * s;
    129139        inline _Istream_str_base;
     140//      _Istream_Rwidth rstr;
    130141}; // _Istream_Rstr
    131142
    132143static inline {
    133144        // read width does not include null terminator
    134         _Istream_Rstr wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, rwd, {.flags.rwd : true}} }; }
     145        _Istream_Rwidth wdi( unsigned int rwd, string_res & s ) { return (_Istream_Rwidth)@{ .s : &s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } }; }
    135146        _Istream_Rstr getline( string_res & s, const char delimiter = '\n' ) {
    136                 return (_Istream_Rstr)@{ &s, {{.delimiters : { delimiter, '\0' } }, -1, {.flags.delimiter : true, .flags.inex : true}} };
    137         }
    138         _Istream_Rstr & getline( _Istream_Rstr & fmt, const char delimiter = '\n' ) {
    139                 fmt.delimiters[0] = delimiter; fmt.delimiters[1] = '\0'; fmt.flags.delimiter = true; fmt.flags.inex = true; return fmt;
    140         }
    141         _Istream_Rstr incl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ &s, {{scanset}, -1, {.flags.inex : false}} }; }
    142         _Istream_Rstr & incl( const char scanset[], _Istream_Rstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = false; return fmt; }
    143         _Istream_Rstr excl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ &s, {{scanset}, -1, {.flags.inex : true}} }; }
    144         _Istream_Rstr & excl( const char scanset[], _Istream_Rstr & fmt ) { fmt.scanset = scanset; fmt.flags.inex = true; return fmt; }
    145         _Istream_Rstr ignore( string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, -1, {.flags.ignore : true}} }; }
    146         _Istream_Rstr & ignore( _Istream_Rstr & fmt ) { fmt.flags.ignore = true; return fmt; }
     147//              return (_Istream_Rstr)@{ { .s : &s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } } };
     148                return (_Istream_Rstr)@{ .s : &s, { {.delimiters : { delimiter, '\0' } }, .wd : -1, {.flags.delimiter : true} } };
     149        }
     150        _Istream_Rstr & getline( _Istream_Rwidth & f, const char delimiter = '\n' ) {
     151                f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Rstr &)f;
     152        }
     153        _Istream_Rquoted quoted( string_res & s, const char Ldelimiter = '\"', const char Rdelimiter = '\0' ) {
     154                return (_Istream_Rquoted)@{ { .s : &s, { {.delimiters : { Ldelimiter, Rdelimiter, '\0' }}, .wd : -1, {.flags.rwd : true} } } };
     155        }
     156        _Istream_Rquoted & quoted( _Istream_Rwidth & f, const char Ldelimiter = '"', const char Rdelimiter = '\0' ) {
     157                f.delimiters[0] = Ldelimiter;  f.delimiters[1] = Rdelimiter;  f.delimiters[2] = '\0';
     158                return (_Istream_Rquoted &)f;
     159        }
     160        _Istream_Rstr incl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : scanset}, .wd : -1, {.flags.inex : false} } }; }
     161        _Istream_Rstr & incl( const char scanset[], _Istream_Rwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Rstr &)f; }
     162        _Istream_Rstr excl( const char scanset[], string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : scanset}, .wd : -1, {.flags.inex : true} } }; }
     163        _Istream_Rstr & excl( const char scanset[], _Istream_Rwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Rstr &)f; }
     164        _Istream_Rstr ignore( string_res & s ) { return (_Istream_Rstr)@{ .s : &s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
     165        _Istream_Rstr & ignore( _Istream_Rwidth & f ) { f.flags.ignore = true; return (_Istream_Rstr &)f; }
     166        _Istream_Rquoted & ignore( _Istream_Rquoted & f ) { f.rstr.flags.ignore = true; return (_Istream_Rquoted &)f; }
     167        _Istream_Rstr & ignore( _Istream_Rstr & f ) { f.flags.ignore = true; return (_Istream_Rstr &)f; }
    147168} // distribution
     169ifstream & ?|?( ifstream & is, _Istream_Rquoted f );
    148170ifstream & ?|?( ifstream & is, _Istream_Rstr f );
    149 void ?|?( ifstream & is, _Istream_Rstr t );
     171static inline ifstream & ?|?( ifstream & is, _Istream_Rwidth f ) { return is | *(_Istream_Rstr *)&f; }
    150172
    151173// Concatenation
  • libcfa/src/fstream.cfa

    ra22d148 r211def2  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 28 09:56:08 2024
    13 // Update Count     : 554
     12// Last Modified On : Thu Feb  1 18:32:15 2024
     13// Update Count     : 575
    1414//
    1515
     
    121121    } // for
    122122        if ( file == 0p ) {
    123                 throw (open_failure){ os };
     123                throwResume (open_failure){ os };
    124124                // abort | IO_MSG "open output file \"" | name | "\"" | nl | strerror( errno );
    125125        } // if
     
    241241    } // for
    242242        if ( file == 0p ) {
    243                 throw (open_failure){ is };
     243                throwResume (open_failure){ is };
    244244                // abort | IO_MSG "open input file \"" | name | "\"" | nl | strerror( errno );
    245245        } // if
     
    295295        va_start( args, format );
    296296
    297         int nargs;
     297        int nargs, tmperrno;
    298298    for () {                                                                                    // no check for EINTR limit waiting for keyboard input
    299                 errno = 0;
    300299                disable_interrupts();
     300                errno = 0;
    301301                nargs = vfscanf( (FILE *)(is.file$), format, args );
     302                tmperrno = errno;
    302303                enable_interrupts();
    303304          if ( nargs != EOF || errno != EINTR ) break;          // timer interrupt ?
     
    308309                } // if
    309310        } // if
     311        if ( tmperrno == ERANGE ) throwResume ExceptionInst( data_range );
    310312        va_end( args );
    311313        return nargs;
  • libcfa/src/iostream.cfa

    ra22d148 r211def2  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 28 11:58:54 2024
    13 // Update Count     : 1917
     12// Last Modified On : Wed Feb  7 22:19:59 2024
     13// Update Count     : 1918
    1414//
    1515
     
    988988          fini: {
    989989                        args = fmt( is, "%*[ \f\n\r\t\v]" );            // remove leading whitespace
    990                         if ( eof( is ) ) break fini;
     990                  if ( eof( is ) ) break fini;
    991991                        char rfmt[4] = { delimiters[0], '%', 'n', '\0' };
    992992                        int len = 0;                                                            // may not be set in fmt
    993993                        args = fmt( is, rfmt, &len );                           // remove leading quote
    994                         if ( len == 0 || eof( is ) ) break fini;
     994                  if ( len == 0 || eof( is ) ) break fini;
    995995
    996996                        // Change the remainder of the read into a getline by reseting the closing delimiter.
  • libcfa/src/iostream.hfa

    ra22d148 r211def2  
    1010// Created On       : Wed May 27 17:56:53 2015
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Sun Jan 28 11:56:29 2024
    13 // Update Count     : 733
     12// Last Modified On : Tue Feb  6 18:35:54 2024
     13// Update Count     : 743
    1414//
    1515
     
    370370// *********************************** exceptions ***********************************
    371371
    372 ExceptionDecl( cstring_length );
    373 ExceptionDecl( missing_data );
     372ExceptionDecl( missing_data );                                                  // read finds no appropriate data
     373ExceptionDecl( cstring_length );                                                // character string size exceeded
     374ExceptionDecl( data_range );                                                    // value too large for numerical type
    374375
    375376// *********************************** manipulators ***********************************
     
    406407        char * s;
    407408        inline _Istream_str_base;
    408 }; // _Istream_Cstr
     409}; // _Istream_Cwidth
    409410
    410411// Restrict nesting of input manipulators to those combinations that make sense.
     412
     413struct _Istream_Cquoted {
     414        _Istream_Cwidth cstr;
     415}; // _Istream_Cquoted
    411416
    412417struct _Istream_Cstr {
     
    414419}; // _Istream_Cstr
    415420
    416 struct _Istream_Cquoted {
    417         _Istream_Cwidth cstr;
    418 }; // _Istream_Cquoted
    419 
    420421static inline {
    421422        // width must include room for null terminator, (gcc) scanf does not allow a 0 width => wd > 1 (1 char and null) and rd > 0 (1 char);
    422423        _Istream_Cwidth wdi( unsigned int wd, char s[] ) {
    423                 if ( wd <= 1 ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character and null terminator
     424                if ( wd <= 1 ) throwResume ExceptionInst( cstring_length ); // minimum 1 character and null terminator
    424425                return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : wd, {.all : 0} } };
    425426        }
    426427        _Istream_Cwidth wdi( unsigned int wd, unsigned int rwd, char s[] ) {
    427                 if ( wd <= 1 || wd <= rwd ) throw (cstring_length){ &cstring_length_vt }; // minimum 1 character, null terminator, plus subset
     428                if ( wd <= 1 || wd <= rwd ) throwResume ExceptionInst( cstring_length ); // minimum 1 character, null terminator, plus subset
    428429                return (_Istream_Cwidth)@{ .s : s, { {.scanset : 0p}, .wd : rwd, {.flags.rwd : true} } };
     430        }
     431        _Istream_Cstr & getline( _Istream_Cwidth & f, const char delimiter = '\n' ) {
     432                f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Cstr &)f;
    429433        }
    430434        _Istream_Cquoted quoted( char & ch, const char Ldelimiter = '\'', const char Rdelimiter = '\0' ) {
     
    435439                return (_Istream_Cquoted &)f;
    436440        }
    437         _Istream_Cstr & getline( _Istream_Cwidth & f, const char delimiter = '\n' ) {
    438                 f.delimiters[0] = delimiter; f.delimiters[1] = '\0'; f.flags.delimiter = true; return (_Istream_Cstr &)f;
    439         }
    440441        _Istream_Cstr & incl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = false; return (_Istream_Cstr &)f; }
    441442        _Istream_Cstr & excl( const char scanset[], _Istream_Cwidth & f ) { f.scanset = scanset; f.flags.inex = true; return (_Istream_Cstr &)f; }
    442         _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cwidth)@{ .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } }; }
     443        _Istream_Cstr ignore( const char s[] ) { return (_Istream_Cstr)@{ { .s : (char *)s, { {.scanset : 0p}, .wd : -1, {.flags.ignore : true} } } }; }
    443444        _Istream_Cstr & ignore( _Istream_Cwidth & f ) { f.flags.ignore = true; return (_Istream_Cstr &)f; }
    444445        _Istream_Cquoted & ignore( _Istream_Cquoted & f ) { f.cstr.flags.ignore = true; return (_Istream_Cquoted &)f; }
     
    450451        istype & ?|?( istype & is, _Istream_Cquoted f );
    451452        istype & ?|?( istype & is, _Istream_Cstr f );
    452         static inline {
    453                 istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; }
    454         } // distribution
     453        static inline istype & ?|?( istype & is, _Istream_Cwidth f ) { return is | *(_Istream_Cstr *)&f; }
    455454} // distribution
    456455
Note: See TracChangeset for help on using the changeset viewer.