source: libcfa/src/containers/string_res.cfa @ 7b0e8b7

ADTast-experimentalenumpthread-emulationqualifiedEnum
Last change on this file since 7b0e8b7 was 7b0e8b7, checked in by Michael Brooks <mlbrooks@…>, 3 years ago

String heap growth implemented

  • Property mode set to 100644
File size: 35.0 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2016 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// string_res -- variable-length, mutable run of text, with resource semantics
8//
9// Author           : Michael L. Brooks
10// Created On       : Fri Sep 03 11:00:00 2021
11// Last Modified By : Michael L. Brooks
12// Last Modified On : Fri Sep 03 11:00:00 2021
13// Update Count     : 1
14//
15
16#include "string_res.hfa"
17#include "string_sharectx.hfa"
18
19#include <stdlib.hfa>  // e.g. malloc
20#include <string.h>    // e.g. strlen
21#include <assert.h>
22
23//######################### VbyteHeap "header" #########################
24
25#ifdef VbyteDebug
26HandleNode *HeaderPtr;
27#endif // VbyteDebug
28
29struct VbyteHeap {
30
31    int NoOfCompactions;                                // number of compactions of the byte area
32    int NoOfExtensions;                                 // number of extensions in the size of the byte area
33    int NoOfReductions;                                 // number of reductions in the size of the byte area
34   
35    int InitSize;                                       // initial number of bytes in the byte-string area
36    int CurrSize;                                       // current number of bytes in the byte-string area
37    char *StartVbyte;                                   // pointer to the `st byte of the start of the byte-string area
38    char *EndVbyte;                                     // pointer to the next byte after the end of the currently used portion of byte-string area
39    void *ExtVbyte;                                     // pointer to the next byte after the end of the byte-string area
40
41    HandleNode Header;                                  // header node for handle list
42}; // VbyteHeap
43
44   
45static inline void compaction( VbyteHeap & );                           // compaction of the byte area
46static inline void garbage( VbyteHeap &, int );                         // garbage collect the byte area
47static inline void extend( VbyteHeap &, int );                  // extend the size of the byte area
48static inline void reduce( VbyteHeap &, int );                  // reduce the size of the byte area
49
50static inline void ?{}( VbyteHeap &, size_t = 1000 );
51static inline void ^?{}( VbyteHeap & );
52static inline void ByteCopy( char *, int, int, char *, int, int ); // copy a block of bytes from one location in the heap to another
53static inline int ByteCmp( char *, int, int, char *, int, int );        // compare 2 blocks of bytes
54static inline char *VbyteAlloc( VbyteHeap &, int );                     // allocate a block bytes in the heap
55
56
57static inline void AddThisAfter( HandleNode &, HandleNode & );
58static inline void DeleteNode( HandleNode & );
59static inline void MoveThisAfter( HandleNode &, const HandleNode & );           // move current handle after parameter handle
60
61
62// Allocate the storage for the variable sized area and intialize the heap variables.
63
64static inline void ?{}( VbyteHeap & this, size_t Size ) with(this) {
65#ifdef VbyteDebug
66    serr | "enter:VbyteHeap::VbyteHeap, this:" | &this | " Size:" | Size;
67#endif // VbyteDebug
68    NoOfCompactions = NoOfExtensions = NoOfReductions = 0;
69    InitSize = CurrSize = Size;
70    StartVbyte = EndVbyte = alloc(CurrSize);
71    ExtVbyte = (void *)( StartVbyte + CurrSize );
72    Header.flink = Header.blink = &Header;
73    Header.ulink = & this;
74#ifdef VbyteDebug
75    HeaderPtr = &Header;
76    serr | "exit:VbyteHeap::VbyteHeap, this:" | &this;
77#endif // VbyteDebug
78} // VbyteHeap
79
80
81// Release the dynamically allocated storage for the byte area.
82
83static inline void ^?{}( VbyteHeap & this ) with(this) {
84    free( StartVbyte );
85} // ~VbyteHeap
86
87
88//######################### HandleNode #########################
89
90
91// Create a handle node. The handle is not linked into the handle list.  This is the responsibilitiy of the handle
92// creator.
93
94static inline void ?{}( HandleNode & this ) with(this) {
95#ifdef VbyteDebug
96    serr | "enter:HandleNode::HandleNode, this:" | &this;
97#endif // VbyteDebug
98    s = 0;
99    lnth = 0;
100#ifdef VbyteDebug
101    serr | "exit:HandleNode::HandleNode, this:" | &this;
102#endif // VbyteDebug
103} // HandleNode
104
105// Create a handle node. The handle is linked into the handle list at the end. This means that this handle will NOT be
106// in order by string address, but this is not a problem because a string with length zero does nothing during garbage
107// collection.
108
109static inline void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {
110#ifdef VbyteDebug
111    serr | "enter:HandleNode::HandleNode, this:" | &this;
112#endif // VbyteDebug
113    s = 0;
114    lnth = 0;
115    ulink = &vh;
116    AddThisAfter( this, *vh.Header.blink );
117#ifdef VbyteDebug
118    serr | "exit:HandleNode::HandleNode, this:" | &this;
119#endif // VbyteDebug
120} // HandleNode
121
122
123// Delete a node from the handle list by unchaining it from the list. If the handle node was allocated dynamically, it
124// is the responsibility of the creator to destroy it.
125
126static inline void ^?{}( HandleNode & this ) with(this) {
127#ifdef VbyteDebug
128    serr | "enter:HandleNode::~HandleNode, this:" | & this;
129    {
130        serr | nlOff;
131        serr | " lnth:" | lnth | " s:" | (void *)s | ",\"";
132        for ( int i = 0; i < lnth; i += 1 ) {
133            serr | s[i];
134        } // for
135        serr | "\" flink:" | flink | " blink:" | blink | nl;
136        serr | nlOn;
137    }
138#endif // VbyteDebug
139    DeleteNode( this );
140} // ~HandleNode
141
142
143//######################### String Sharing Context #########################
144
145static string_sharectx * ambient_string_sharectx;               // fickle top of stack
146static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack
147
148void ?{}( string_sharectx & this, StringSharectx_Mode mode ) with( this ) {
149    (older){ ambient_string_sharectx };
150    if ( mode == NEW_SHARING ) {
151        (activeHeap){ new( (size_t) 1000 ) };
152    } else {
153        verify( mode == NO_SHARING );
154        (activeHeap){ 0p };
155    }
156    ambient_string_sharectx = & this;
157}
158
159void ^?{}( string_sharectx & this ) with( this ) {
160    if ( activeHeap ) delete( activeHeap );
161
162    // unlink this from older-list starting from ambient_string_sharectx
163    // usually, this==ambient_string_sharectx and the loop runs zero times
164    string_sharectx *& c = ambient_string_sharectx;
165    while ( c != &this ) &c = &c->older;              // find this
166    c = this.older;                                   // unlink
167}
168
169//######################### String Resource #########################
170
171
172VbyteHeap * DEBUG_string_heap() {
173    assert( ambient_string_sharectx->activeHeap && "No sharing context is active" );
174    return ambient_string_sharectx->activeHeap;
175}
176
177size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) {
178    return ((char*)heap->ExtVbyte) - heap->EndVbyte;
179}
180
181size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) {
182    return heap->CurrSize;
183}
184
185const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
186    return heap->StartVbyte;
187}
188
189// Returns the size of the string in bytes
190size_t size(const string_res &s) with(s) {
191    return Handle.lnth;
192}
193
194// Output operator
195ofstream & ?|?(ofstream &out, const string_res &s) {
196    // Store auto-newline state so it can be restored
197    bool anl = getANL$(out);
198    nlOff(out);
199    for (size_t i = 0; i < s.Handle.lnth; i++) {
200        out | s[i];
201    }
202    out | sep;
203    // Re-apply newlines after done, for chaining version
204    if (anl) nlOn(out);
205    return out;
206}
207
208void ?|?(ofstream &out, const string_res &s) {
209    // Store auto-newline state so it can be restored
210    bool anl = getANL$(out);
211    nlOff(out);
212    for (size_t i = 0; i < s.Handle.lnth; i++) {
213        // Need to re-apply on the last output operator, for whole-statement version
214        if (anl && i == s.Handle.lnth-1) nlOn(out);
215        out | s[i];
216    }
217    return out;
218}
219
220// Empty constructor
221void ?{}(string_res &s) with(s) {
222    if( ambient_string_sharectx->activeHeap ) {
223        (Handle){ * ambient_string_sharectx->activeHeap };
224        (shareEditSet_owns_ulink){ false };
225    } else {
226        (Handle){ * new( (size_t) 10 ) };  // TODO: can I lazily avoid allocating for empty string
227        (shareEditSet_owns_ulink){ true };
228    }
229    s.shareEditSet_prev = &s;
230    s.shareEditSet_next = &s;
231}
232
233static inline void eagerCopyCtorHelper(string_res &s, const char* rhs, size_t rhslnth) with(s) {
234    if( ambient_string_sharectx->activeHeap ) {
235        (Handle){ * ambient_string_sharectx->activeHeap };
236        (shareEditSet_owns_ulink){ false };
237    } else {
238        (Handle){ * new( rhslnth ) };
239        (shareEditSet_owns_ulink){ true };
240    }
241    Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
242    Handle.lnth = rhslnth;
243    for ( int i = 0; i < rhslnth; i += 1 ) {            // copy characters
244        Handle.s[i] = rhs[i];
245    } // for
246    s.shareEditSet_prev = &s;
247    s.shareEditSet_next = &s;
248}
249
250// Constructor from a raw buffer and size
251void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
252    eagerCopyCtorHelper(s, rhs, rhslnth);
253}
254
255// String literal constructor
256void ?{}(string_res &s, const char* rhs) {
257    (s){ rhs, strlen(rhs) };
258}
259
260// General copy constructor
261void ?{}(string_res &s, const string_res & s2, StrResInitMode mode, size_t start, size_t end ) {
262
263    verify( start <= end && end <= s2.Handle.lnth );
264
265    if (s2.Handle.ulink == ambient_string_sharectx->activeHeap) {
266        // same heap: allow overlap
267        (s.Handle){};
268        s.Handle.s = s2.Handle.s + start;
269        s.Handle.lnth = end - start;
270        s.Handle.ulink = ambient_string_sharectx->activeHeap;
271        (s.shareEditSet_owns_ulink){ false };
272        AddThisAfter(s.Handle, s2.Handle );                     // insert this handle after rhs handle
273        // ^ bug?  skip others at early point in string
274   
275        if (mode == COPY_VALUE) {
276            // make s alone in its shareEditSet
277            s.shareEditSet_prev = &s;
278            s.shareEditSet_next = &s;
279        } else {
280            verify( mode == SHARE_EDITS );
281
282            // s2 is logically const but not implementation const
283            string_res & s2mod = (string_res &) s2;
284
285            // insert s after s2 on shareEditSet
286            s.shareEditSet_next = s2mod.shareEditSet_next;
287            s.shareEditSet_prev = &s2mod;
288            s.shareEditSet_next->shareEditSet_prev = &s;
289            s.shareEditSet_prev->shareEditSet_next = &s;
290        }
291    } else {
292        // crossing heaps: eager copy
293        assert( mode == COPY_VALUE && "need to solidify context-crossing rules for requesting shared edits");
294        eagerCopyCtorHelper(s, s2.Handle.s + start, end - start);
295        verify(s.shareEditSet_prev == &s);
296        verify(s.shareEditSet_next == &s);
297    }
298}
299
300static void assignEditSet(string_res & this, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer,
301    char * resultSesStart,
302    size_t resultSesLnth,
303    HandleNode * resultPadPosition, size_t bsize ) {
304
305    char * beforeBegin = shareEditSetStartPeer->Handle.s;
306    size_t beforeLen = this.Handle.s - beforeBegin;
307
308    char * afterBegin = this.Handle.s + this.Handle.lnth;
309    size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
310
311    size_t oldLnth = this.Handle.lnth;
312
313    this.Handle.s = resultSesStart + beforeLen;
314    this.Handle.lnth = bsize;
315    MoveThisAfter( this.Handle, *resultPadPosition );
316
317    // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
318    char *limit = resultSesStart + resultSesLnth;
319    for (string_res * p = this.shareEditSet_next; p != &this; p = p->shareEditSet_next) {
320        verify (p->Handle.s >= beforeBegin);
321        if ( p->Handle.s >= afterBegin ) {
322            verify ( p->Handle.s <= afterBegin + afterLen );
323            verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
324            // p starts after the edit
325            // take start and end as end-anchored
326            size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
327            p->Handle.s = limit - startOffsetFromEnd;
328            // p->Handle.lnth unaffected
329        } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
330            // p starts before, or at the start of, the edit
331            if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
332                // p ends before the edit
333                // take end as start-anchored too
334                // p->Handle.lnth unaffected
335            } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
336                // p ends during the edit; p does not include the last character replaced
337                // clip end of p to end at start of edit
338                p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
339            } else {
340                // p ends after the edit
341                verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
342                // take end as end-anchored
343                // stretch-shrink p according to the edit
344                p->Handle.lnth += this.Handle.lnth;
345                p->Handle.lnth -= oldLnth;
346            }
347            // take start as start-anchored
348            size_t startOffsetFromStart = p->Handle.s - beforeBegin;
349            p->Handle.s = resultSesStart + startOffsetFromStart;
350        } else {
351            verify ( p->Handle.s < afterBegin );
352            // p starts during the edit
353            verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
354            if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
355                // p ends during the edit; p does not include the last character replaced
356                // set p to empty string at start of edit
357                p->Handle.s = this.Handle.s;
358                p->Handle.lnth = 0;
359            } else {
360                // p includes the end of the edit
361                // clip start of p to start at end of edit
362                int charsToClip = afterBegin - p->Handle.s;
363                p->Handle.s = this.Handle.s + this.Handle.lnth;
364                p->Handle.lnth -= charsToClip;
365            }
366        }
367        MoveThisAfter( p->Handle, *resultPadPosition ); // move substring handle to maintain sorted order by string position
368    }
369}
370
371static void assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) {
372
373    // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
374    string_res * shareEditSetStartPeer = & this;
375    string_res * shareEditSetEndPeer = & this;
376    for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
377        if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
378            shareEditSetStartPeer = editPeer;
379        }
380        if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
381            shareEditSetEndPeer = editPeer;
382        }
383    }
384
385    verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s );
386    size_t editSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s;
387    verify( editSetLength >= this.Handle.lnth );
388
389
390    if ( this.Handle.lnth == editSetLength                // the entire run of the share-edit set is being overwritten: SES's result will only use characters from the source string
391        && & valSrc                                       // sourcing from a managed string
392        && valSrc.Handle.ulink == this.Handle.ulink  ) {  // sourcing from same heap
393
394        assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
395            valSrc.Handle.s,
396            valSrc.Handle.lnth,
397            &((string_res&)valSrc).Handle, bsize);
398       
399    } else {
400
401        // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
402        // `this` occurs in the middle of it, to be replaced
403        // build up the new text in `pasting`
404
405        // super private string ctor: ignore ambient context
406        void ?{}( string_res &s, VbyteHeap & heap, const char* rhs, size_t rhslnth ) with(s) {
407            (Handle){ heap };
408            Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
409            Handle.lnth = rhslnth;
410            (s.shareEditSet_owns_ulink){ false };
411            for ( int i = 0; i < rhslnth; i += 1 ) {            // copy characters
412                Handle.s[i] = rhs[i];
413            } // for
414            s.shareEditSet_prev = &s;
415            s.shareEditSet_next = &s;
416        }
417
418        // we are only overwriting a proper substring of some string: need to mash characters from old and new together
419        // OR we are importing characters: need to copy eagerly
420        string_res pasting = {
421            * this.Handle.ulink,                               // maintain same heap, regardless of context
422            shareEditSetStartPeer->Handle.s,                   // start of SES
423            this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
424        append( pasting,
425            buffer,                                            // start of replacement for this
426            bsize );                                           // length of replacement for this
427        append( pasting,
428            this.Handle.s + this.Handle.lnth,                  // start of SES after this
429            shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
430            (this.Handle.s + this.Handle.lnth) );              // length of SES, after this
431
432        // The above string building can trigger compaction.
433        // The reference points (that are arguments of the string building) may move during that building.
434        // From this point on, they are stable.
435
436        assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer,
437            pasting.Handle.s,
438            pasting.Handle.lnth,
439            &pasting.Handle, bsize);
440    }
441
442    // So now, capture their values for use in the overlap cases, below.
443    // Do not factor these definitions with the arguments used in string building above.
444
445}
446
447void assign(string_res &this, const char* buffer, size_t bsize) {
448    assign_(this, buffer, bsize, *0p);
449}
450
451void ?=?(string_res &s, const char* other) {
452    assign(s, other, strlen(other));
453}
454
455void ?=?(string_res &s, char other) {
456    assign(s, &other, 1);
457}
458
459// Copy assignment operator
460void ?=?(string_res & this, const string_res & rhs) with( this ) {
461    assign_(this, rhs.Handle.s, rhs.Handle.lnth, rhs);
462}
463
464void ?=?(string_res & this, string_res & rhs) with( this ) {
465    const string_res & rhs2 = rhs;
466    this = rhs2;
467}
468
469
470// Destructor
471void ^?{}(string_res &s) with(s) {
472    // much delegated to implied ^VbyteSM
473
474    // sever s from its share-edit peers, if any (four no-ops when already solo)
475    s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
476    s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
477    // s.shareEditSet_next = &s;
478    // s.shareEditSet_prev = &s;
479
480    if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out
481        delete( s.Handle.ulink );
482    }
483}
484
485
486// Returns the character at the given index
487// With unicode support, this may be different from just the byte at the given
488// offset from the start of the string.
489char ?[?](const string_res &s, size_t index) with(s) {
490    //TODO: Check if index is valid (no exceptions yet)
491    return Handle.s[index];
492}
493
494void assignAt(const string_res &s, size_t index, char val) {
495    string_res editZone = { s, SHARE_EDITS, index, index+1 };
496    assign(editZone, &val, 1);
497}
498
499
500///////////////////////////////////////////////////////////////////
501// Concatenation
502
503void append(string_res &str1, const char * buffer, size_t bsize) {
504    size_t clnth = size(str1) + bsize;
505    if ( str1.Handle.s + size(str1) == buffer ) { // already juxtapose ?
506        // no-op
507    } else {                                            // must copy some text
508        if ( str1.Handle.s + size(str1) == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
509            VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
510        } else {                                        // copy the two parts
511            char * str1oldBuf = str1.Handle.s;
512            str1.Handle.s = VbyteAlloc( *str1.Handle.ulink, clnth );
513            ByteCopy( str1.Handle.s, 0, str1.Handle.lnth, str1oldBuf, 0, str1.Handle.lnth);
514        } // if
515        ByteCopy( str1.Handle.s, str1.Handle.lnth, bsize, (char*)buffer, 0, (int)bsize);
516        //       VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth
517    } // if
518    str1.Handle.lnth = clnth;
519}
520
521void ?+=?(string_res &str1, const string_res &str2) {
522    append( str1, str2.Handle.s, str2.Handle.lnth );
523}
524
525void ?+=?(string_res &s, char other) {
526    append( s, &other, 1 );
527}
528
529void ?+=?(string_res &s, const char* other) {
530    append( s, other, strlen(other) );
531}
532
533
534
535
536//////////////////////////////////////////////////////////
537// Comparisons
538
539
540bool ?==?(const string_res &s1, const string_res &s2) {
541    return ByteCmp( s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
542}
543
544bool ?!=?(const string_res &s1, const string_res &s2) {
545    return !(s1 == s2);
546}
547bool ?==?(const string_res &s, const char* other) {
548    string_res sother = other;
549    return s == sother;
550}
551bool ?!=?(const string_res &s, const char* other) {
552    return !(s == other);
553}
554
555
556//////////////////////////////////////////////////////////
557// Search
558
559bool contains(const string_res &s, char ch) {
560    for (i; size(s)) {
561        if (s[i] == ch) return true;
562    }
563    return false;
564}
565
566int find(const string_res &s, char search) {
567    for (i; size(s)) {
568        if (s[i] == search) return i;
569    }
570    return size(s);
571}
572
573    /* Remaining implementations essentially ported from Sunjay's work */
574
575int find(const string_res &s, const string_res &search) {
576    return find(s, search.Handle.s, search.Handle.lnth);
577}
578
579int find(const string_res &s, const char* search) {
580    return find(s, search, strlen(search));
581}
582
583int find(const string_res &s, const char* search, size_t searchsize) {
584    // FIXME: This is a naive algorithm. We probably want to switch to someting
585    // like Boyer-Moore in the future.
586    // https://en.wikipedia.org/wiki/String_searching_algorithm
587
588    // Always find the empty string
589    if (searchsize == 0) {
590        return 0;
591    }
592
593    for (size_t i = 0; i < s.Handle.lnth; i++) {
594        size_t remaining = s.Handle.lnth - i;
595        // Never going to find the search string if the remaining string is
596        // smaller than search
597        if (remaining < searchsize) {
598            break;
599        }
600
601        bool matched = true;
602        for (size_t j = 0; j < searchsize; j++) {
603            if (search[j] != s.Handle.s[i + j]) {
604                matched = false;
605                break;
606            }
607        }
608        if (matched) {
609            return i;
610        }
611    }
612
613    return s.Handle.lnth;
614}
615
616bool includes(const string_res &s, const string_res &search) {
617    return includes(s, search.Handle.s, search.Handle.lnth);
618}
619
620bool includes(const string_res &s, const char* search) {
621    return includes(s, search, strlen(search));
622}
623
624bool includes(const string_res &s, const char* search, size_t searchsize) {
625    return find(s, search, searchsize) < s.Handle.lnth;
626}
627
628bool startsWith(const string_res &s, const string_res &prefix) {
629    return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
630}
631
632bool startsWith(const string_res &s, const char* prefix) {
633    return startsWith(s, prefix, strlen(prefix));
634}
635
636bool startsWith(const string_res &s, const char* prefix, size_t prefixsize) {
637    if (s.Handle.lnth < prefixsize) {
638        return false;
639    }
640    return memcmp(s.Handle.s, prefix, prefixsize) == 0;
641}
642
643bool endsWith(const string_res &s, const string_res &suffix) {
644    return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
645}
646
647bool endsWith(const string_res &s, const char* suffix) {
648    return endsWith(s, suffix, strlen(suffix));
649}
650
651bool endsWith(const string_res &s, const char* suffix, size_t suffixsize) {
652    if (s.Handle.lnth < suffixsize) {
653        return false;
654    }
655    // Amount to offset the bytes pointer so that we are comparing the end of s
656    // to suffix. s.bytes + offset should be the first byte to compare against suffix
657    size_t offset = s.Handle.lnth - suffixsize;
658    return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
659}
660
661    /* Back to Mike's work */
662
663
664///////////////////////////////////////////////////////////////////////////
665// charclass, include, exclude
666
667void ?{}( charclass_res & this, const string_res & chars) {
668    (this){ chars.Handle.s, chars.Handle.lnth };
669}
670
671void ?{}( charclass_res & this, const char * chars ) {
672    (this){ chars, strlen(chars) };
673}
674
675void ?{}( charclass_res & this, const char * chars, size_t charssize ) {
676    (this.chars){ chars, charssize };
677    // now sort it ?
678}
679
680void ^?{}( charclass_res & this ) {
681    ^(this.chars){};
682}
683
684static bool test( const charclass_res & mask, char c ) {
685    // instead, use sorted char list?
686    return contains( mask.chars, c );
687}
688
689int exclude(const string_res &s, const charclass_res &mask) {
690    for (int i = 0; i < size(s); i++) {
691        if ( test(mask, s[i]) ) return i;
692    }
693    return size(s);
694}
695
696int include(const string_res &s, const charclass_res &mask) {
697    for (int i = 0; i < size(s); i++) {
698        if ( ! test(mask, s[i]) ) return i;
699    }
700    return size(s);
701}
702
703//######################### VbyteHeap "implementation" #########################
704
705
706// Add a new HandleNode node n after the current HandleNode node.
707
708static inline void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
709#ifdef VbyteDebug
710    serr | "enter:AddThisAfter, this:" | &this | " n:" | &n;
711#endif // VbyteDebug
712    verify( n.ulink != 0p );
713    verify( this.ulink == n.ulink );
714    flink = n.flink;
715    blink = &n;
716    n.flink->blink = &this;
717    n.flink = &this;
718#ifdef VbyteDebug
719    {
720                serr | "HandleList:";
721                serr | nlOff;
722                for ( HandleNode *ni = HeaderPtr->flink; ni != HeaderPtr; ni = ni->flink ) {
723                        serr | "\tnode:" | ni | " lnth:" | ni->lnth | " s:" | (void *)ni->s | ",\"";
724                        for ( int i = 0; i < ni->lnth; i += 1 ) {
725                                serr | ni->s[i];
726                        } // for
727                        serr | "\" flink:" | ni->flink | " blink:" | ni->blink | nl;
728                } // for
729                serr | nlOn;
730    }
731    serr | "exit:AddThisAfter";
732#endif // VbyteDebug
733} // AddThisAfter
734
735
736// Delete the current HandleNode node.
737
738static inline void DeleteNode( HandleNode & this ) with(this) {
739#ifdef VbyteDebug
740    serr | "enter:DeleteNode, this:" | &this;
741#endif // VbyteDebug
742    flink->blink = blink;
743    blink->flink = flink;
744#ifdef VbyteDebug
745    serr | "exit:DeleteNode";
746#endif // VbyteDebug
747} //  DeleteNode
748
749
750
751// Allocates specified storage for a string from byte-string area. If not enough space remains to perform the
752// allocation, the garbage collection routine is called and a second attempt is made to allocate the space. If the
753// second attempt fails, a further attempt is made to create a new, larger byte-string area.
754
755static inline char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
756#ifdef VbyteDebug
757    serr | "enter:VbyteAlloc, size:" | size;
758#endif // VbyteDebug
759    uintptr_t NoBytes;
760    char *r;
761
762    NoBytes = ( uintptr_t )EndVbyte + size;
763    if ( NoBytes > ( uintptr_t )ExtVbyte ) {            // enough room for new byte-string ?
764                garbage( this, size );                                  // firer up the garbage collector
765                NoBytes = ( uintptr_t )EndVbyte + size;         // try again
766                if ( NoBytes > ( uintptr_t )ExtVbyte ) {        // enough room for new byte-string ?
767            assert( 0 && "garbage run did not free up required space" );
768                } // if
769    } // if
770    r = EndVbyte;
771    EndVbyte += size;
772#ifdef VbyteDebug
773    serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
774#endif // VbyteDebug
775    return r;
776} // VbyteAlloc
777
778
779// Move an existing HandleNode node h somewhere after the current HandleNode node so that it is in ascending order by
780// the address in the byte string area.
781
782static inline void MoveThisAfter( HandleNode & this, const HandleNode  & h ) with(this) {
783#ifdef VbyteDebug
784    serr | "enter:MoveThisAfter, this:" | & this | " h:" | & h;
785#endif // VbyteDebug
786    verify( h.ulink != 0p );
787    verify( this.ulink == h.ulink );
788    if ( s < h.s ) {                                    // check argument values
789                // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
790                //      | ( h->s ) | " and keep handles in ascending order";
791                // exit(-1 );
792                verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
793    } // if
794
795    HandleNode *i;
796    for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
797    if ( & this != i->blink ) {
798                DeleteNode( this );
799                AddThisAfter( this, *i->blink );
800    } // if
801#ifdef VbyteDebug
802    {
803        serr | "HandleList:";
804        serr | nlOff;
805        for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
806            serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
807            for ( int i = 0; i < n->lnth; i += 1 ) {
808                serr | n->s[i];
809            } // for
810            serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
811        } // for
812        serr | nlOn;
813    }
814    serr | "exit:MoveThisAfter";
815#endif // VbyteDebug
816} // MoveThisAfter
817
818
819
820
821
822//######################### VbyteHeap #########################
823
824// Move characters from one location in the byte-string area to another. The routine handles the following situations:
825//
826// if the |Src| > |Dst| => truncate
827// if the |Dst| > |Src| => pad Dst with blanks
828
829void ByteCopy( char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth ) {
830    for ( int i = 0; i < DstLnth; i += 1 ) {
831      if ( i == SrcLnth ) {                             // |Dst| > |Src|
832            for ( ; i < DstLnth; i += 1 ) {             // pad Dst with blanks
833                Dst[DstStart + i] = ' ';
834            } // for
835            break;
836        } // exit
837        Dst[DstStart + i] = Src[SrcStart + i];
838    } // for
839} // ByteCopy
840
841// Compare two byte strings in the byte-string area. The routine returns the following values:
842//
843// 1 => Src1-byte-string > Src2-byte-string
844// 0 => Src1-byte-string = Src2-byte-string
845// -1 => Src1-byte-string < Src2-byte-string
846
847int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth )  {
848#ifdef VbyteDebug
849    serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
850#endif // VbyteDebug
851    int cmp;
852
853    CharZip: for ( int i = 0; ; i += 1 ) {
854        if ( i == Src2Lnth - 1 ) {
855            for ( ; ; i += 1 ) {
856                if ( i == Src1Lnth - 1 ) {
857                    cmp = 0;
858                    break CharZip;
859                } // exit
860                if ( Src1[Src1Start + i] != ' ') {
861                        // SUSPECTED BUG:  this could be be why Peter got the bug report about == " "  (why is this case here at all?)
862                    cmp = 1;
863                    break CharZip;
864                } // exit
865            } // for
866        } // exit
867        if ( i == Src1Lnth - 1 ) {
868            for ( ; ; i += 1 ) {
869                if ( i == Src2Lnth - 1 ) {
870                    cmp = 0;
871                    break CharZip;
872                } // exit
873                if ( Src2[Src2Start + i] != ' ') {
874                    cmp = -1;
875                    break CharZip;
876                } // exit
877            } // for
878        } // exit
879      if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
880            cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
881            break CharZip;
882        } // exit
883    } // for
884#ifdef VbyteDebug
885    serr | "exit:ByteCmp, cmp:" | cmp;
886#endif // VbyteDebug
887    return cmp;
888} // ByteCmp
889
890
891// The compaction moves all of the byte strings currently in use to the beginning of the byte-string area and modifies
892// the handles to reflect the new positions of the byte strings. Compaction assumes that the handle list is in ascending
893// order by pointers into the byte-string area.  The strings associated with substrings do not have to be moved because
894// the containing string has been moved. Hence, they only require that their string pointers be adjusted.
895
896void compaction(VbyteHeap & this) with(this) {
897    HandleNode *h;
898    char *obase, *nbase, *limit;
899   
900    NoOfCompactions += 1;
901    EndVbyte = StartVbyte;
902    h = Header.flink;                                   // ignore header node
903    for (;;) {
904                ByteCopy( EndVbyte, 0, h->lnth, h->s, 0, h->lnth );
905                obase = h->s;
906                h->s = EndVbyte;
907                nbase = h->s;
908                EndVbyte += h->lnth;
909                limit = obase + h->lnth;
910                h = h->flink;
911               
912                // check if any substrings are allocated within a string
913               
914                for (;;) {
915                        if ( h == &Header ) break;                      // end of header list ?
916                        if ( h->s >= limit ) break;                     // outside of current string ?
917                        h->s = nbase + (( uintptr_t )h->s - ( uintptr_t )obase );
918                        h = h->flink;
919                } // for
920                if ( h == &Header ) break;                      // end of header list ?
921    } // for
922} // compaction
923
924
925// Garbage determines the amount of free space left in the heap and then reduces, leave the same, or extends the size of
926// the heap.  The heap is then compacted in the existing heap or into the newly allocated heap.
927
928void garbage(VbyteHeap & this, int minreq ) with(this) {
929#ifdef VbyteDebug
930    serr | "enter:garbage";
931    {
932                serr | "HandleList:";
933                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
934                        serr | nlOff;
935                        serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
936                        for ( int i = 0; i < n->lnth; i += 1 ) {
937                                serr | n->s[i];
938                        } // for
939                        serr | nlOn;
940                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
941                } // for
942    }
943#endif // VbyteDebug
944    int AmountUsed, AmountFree;
945
946    AmountUsed = 0;
947    for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
948                AmountUsed += i->lnth;
949    } // for
950    AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
951   
952    if ( ( double ) AmountFree < ( CurrSize * 0.1 ) || AmountFree < minreq ) {  // free space less than 10%  or not enough to serve cur request
953
954                extend( this, max( CurrSize, minreq ) );                                // extend the heap
955
956                        //  Peter says, "This needs work before it should be used."
957                        //  } else if ( AmountFree > CurrSize / 2 ) {           // free space greater than 3 times the initial allocation ?
958                        //              reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
959
960    } // if
961    compaction(this);                                   // compact the byte area, in the same or new heap area
962#ifdef VbyteDebug
963    {
964                serr | "HandleList:";
965                for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
966                        serr | nlOff;
967                        serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
968                        for ( int i = 0; i < n->lnth; i += 1 ) {
969                                serr | n->s[i];
970                        } // for
971                        serr | nlOn;
972                        serr | "\" flink:" | n->flink | " blink:" | n->blink;
973                } // for
974    }
975    serr | "exit:garbage";
976#endif // VbyteDebug
977} // garbage
978
979#undef VbyteDebug
980
981
982
983// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
984// area is deleted.
985
986void extend( VbyteHeap & this, int size ) with (this) {
987#ifdef VbyteDebug
988    serr | "enter:extend, size:" | size;
989#endif // VbyteDebug
990    char *OldStartVbyte;
991
992    NoOfExtensions += 1;
993    OldStartVbyte = StartVbyte;                         // save previous byte area
994   
995    CurrSize += size > InitSize ? size : InitSize;      // minimum extension, initial size
996    StartVbyte = EndVbyte = alloc(CurrSize);
997    ExtVbyte = (void *)( StartVbyte + CurrSize );
998    compaction(this);                                   // copy from old heap to new & adjust pointers to new heap
999    free( OldStartVbyte );                              // release old heap
1000#ifdef VbyteDebug
1001    serr | "exit:extend, CurrSize:" | CurrSize;
1002#endif // VbyteDebug
1003} // extend
1004
1005//WIP
1006#if 0
1007
1008// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
1009// area is deleted.
1010
1011void VbyteHeap::reduce( int size ) {
1012#ifdef VbyteDebug
1013    serr | "enter:reduce, size:" | size;
1014#endif // VbyteDebug
1015    char *OldStartVbyte;
1016
1017    NoOfReductions += 1;
1018    OldStartVbyte = StartVbyte;                         // save previous byte area
1019   
1020    CurrSize -= size;
1021    StartVbyte = EndVbyte = new char[CurrSize];
1022    ExtVbyte = (void *)( StartVbyte + CurrSize );
1023    compaction();                                       // copy from old heap to new & adjust pointers to new heap
1024    delete  OldStartVbyte;                              // release old heap
1025#ifdef VbyteDebug
1026    serr | "exit:reduce, CurrSize:" | CurrSize;
1027#endif // VbyteDebug
1028} // reduce
1029
1030
1031#endif
Note: See TracBrowser for help on using the repository browser.