source: libcfa/src/containers/string_res.cfa@ 0f781fb8

ADT ast-experimental enum pthread-emulation qualifiedEnum stuck-waitfor-destruct
Last change on this file since 0f781fb8 was 0f781fb8, checked in by Michael Brooks <mlbrooks@…>, 5 years ago

Refactoring of string internals. Existing tests pass.

Adding tracking for multiple string heaps, or "scratchpads."
Cases of allocating across different pad contexts aren't implemented yet.
Adding basic controls to manage these contexts, which lead to expected assertion failures
at unimplemented cases.

  • Property mode set to 100644
File size: 31.9 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 & ); // 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 &, int = 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, int 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( 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
181const char * DEBUG_string_heap_start( VbyteHeap * heap ) {
182 return heap->StartVbyte;
183}
184
185
186// Returns the size of the string in bytes
187size_t size(const string_res &s) with(s) {
188 return Handle.lnth;
189}
190
191// Output operator
192ofstream & ?|?(ofstream &out, const string_res &s) {
193 // Store auto-newline state so it can be restored
194 bool anl = getANL$(out);
195 nlOff(out);
196 for (size_t i = 0; i < s.Handle.lnth; i++) {
197 out | s[i];
198 }
199 out | sep;
200 // Re-apply newlines after done, for chaining version
201 if (anl) nlOn(out);
202 return out;
203}
204
205void ?|?(ofstream &out, const string_res &s) {
206 // Store auto-newline state so it can be restored
207 bool anl = getANL$(out);
208 nlOff(out);
209 for (size_t i = 0; i < s.Handle.lnth; i++) {
210 // Need to re-apply on the last output operator, for whole-statement version
211 if (anl && i == s.Handle.lnth-1) nlOn(out);
212 out | s[i];
213 }
214 return out;
215}
216
217// Empty constructor
218void ?{}(string_res &s) with(s) {
219 assert( ambient_string_sharectx->activeHeap && "Need to implement private contexts" );
220 (Handle){ * ambient_string_sharectx->activeHeap };
221 s.shareEditSet_prev = &s;
222 s.shareEditSet_next = &s;
223}
224
225// Constructor from a raw buffer and size
226void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) {
227 assert( ambient_string_sharectx->activeHeap && "Need to implement private contexts" );
228 (Handle){ * ambient_string_sharectx->activeHeap };
229 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth);
230 Handle.lnth = rhslnth;
231 for ( int i = 0; i < rhslnth; i += 1 ) { // copy characters
232 Handle.s[i] = rhs[i];
233 } // for
234 s.shareEditSet_prev = &s;
235 s.shareEditSet_next = &s;
236}
237
238// String literal constructor
239void ?{}(string_res &s, const char* rhs) {
240 (s){ rhs, strlen(rhs) };
241}
242
243// General copy constructor
244void ?{}(string_res &s, const string_res & s2, StrResInitMode mode, size_t start, size_t end ) {
245
246 assert( ambient_string_sharectx->activeHeap && "Need to implement private contexts" );
247 assert( s2.Handle.ulink == ambient_string_sharectx->activeHeap && "need to implement context crossing");
248
249 verify( start <= end && end <= s2.Handle.lnth );
250
251 (s.Handle){};
252 s.Handle.s = s2.Handle.s + start;
253 s.Handle.lnth = end - start;
254 s.Handle.ulink = ambient_string_sharectx->activeHeap;
255 AddThisAfter(s.Handle, s2.Handle ); // insert this handle after rhs handle
256 // ^ bug? skip others at early point in string
257
258 if (mode == COPY_VALUE) {
259 // make s alone in its shareEditSet
260 s.shareEditSet_prev = &s;
261 s.shareEditSet_next = &s;
262 } else {
263 verify( mode == SHARE_EDITS );
264
265 // s2 is logically const but not implementation const
266 string_res & s2mod = (string_res &) s2;
267
268 // insert s after s2 on shareEditSet
269 s.shareEditSet_next = s2mod.shareEditSet_next;
270 s.shareEditSet_prev = &s2mod;
271 s.shareEditSet_next->shareEditSet_prev = &s;
272 s.shareEditSet_prev->shareEditSet_next = &s;
273 }
274}
275
276void assign(string_res &this, const char* buffer, size_t bsize) {
277
278 // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs
279 string_res * shareEditSetStartPeer = & this;
280 string_res * shareEditSetEndPeer = & this;
281 for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) {
282 if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) {
283 shareEditSetStartPeer = editPeer;
284 }
285 if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) {
286 shareEditSetEndPeer = editPeer;
287 }
288 }
289
290 // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer
291 // `this` occurs in the middle of it, to be replaced
292 // build up the new text in `pasting`
293
294 string_res pasting = {
295 shareEditSetStartPeer->Handle.s, // start of SES
296 this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this
297 append( pasting,
298 buffer, // start of replacement for this
299 bsize ); // length of replacement for this
300 append( pasting,
301 this.Handle.s + this.Handle.lnth, // start of SES after this
302 shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth -
303 (this.Handle.s + this.Handle.lnth) ); // length of SES, after this
304
305 // The above string building can trigger compaction.
306 // The reference points (that are arguments of the string building) may move during that building.
307 // From this point on, they are stable.
308 // So now, capture their values for use in the overlap cases, below.
309 // Do not factor these definitions with the arguments used above.
310
311 char * beforeBegin = shareEditSetStartPeer->Handle.s;
312 size_t beforeLen = this.Handle.s - beforeBegin;
313
314 char * afterBegin = this.Handle.s + this.Handle.lnth;
315 size_t afterLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - afterBegin;
316
317 size_t oldLnth = this.Handle.lnth;
318
319 this.Handle.s = pasting.Handle.s + beforeLen;
320 this.Handle.lnth = bsize;
321 MoveThisAfter( this.Handle, pasting.Handle );
322
323 // adjust all substring string and handle locations, and check if any substring strings are outside the new base string
324 char *limit = pasting.Handle.s + pasting.Handle.lnth;
325 for (string_res * p = this.shareEditSet_next; p != &this; p = p->shareEditSet_next) {
326 verify (p->Handle.s >= beforeBegin);
327 if ( p->Handle.s >= afterBegin ) {
328 verify ( p->Handle.s <= afterBegin + afterLen );
329 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
330 // p starts after the edit
331 // take start and end as end-anchored
332 size_t startOffsetFromEnd = afterBegin + afterLen - p->Handle.s;
333 p->Handle.s = limit - startOffsetFromEnd;
334 // p->Handle.lnth unaffected
335 } else if ( p->Handle.s <= beforeBegin + beforeLen ) {
336 // p starts before, or at the start of, the edit
337 if ( p->Handle.s + p->Handle.lnth <= beforeBegin + beforeLen ) {
338 // p ends before the edit
339 // take end as start-anchored too
340 // p->Handle.lnth unaffected
341 } else if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
342 // p ends during the edit; p does not include the last character replaced
343 // clip end of p to end at start of edit
344 p->Handle.lnth = beforeLen - ( p->Handle.s - beforeBegin );
345 } else {
346 // p ends after the edit
347 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );
348 // take end as end-anchored
349 // stretch-shrink p according to the edit
350 p->Handle.lnth += this.Handle.lnth;
351 p->Handle.lnth -= oldLnth;
352 }
353 // take start as start-anchored
354 size_t startOffsetFromStart = p->Handle.s - beforeBegin;
355 p->Handle.s = pasting.Handle.s + startOffsetFromStart;
356 } else {
357 verify ( p->Handle.s < afterBegin );
358 // p starts during the edit
359 verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );
360 if ( p->Handle.s + p->Handle.lnth < afterBegin ) {
361 // p ends during the edit; p does not include the last character replaced
362 // set p to empty string at start of edit
363 p->Handle.s = this.Handle.s;
364 p->Handle.lnth = 0;
365 } else {
366 // p includes the end of the edit
367 // clip start of p to start at end of edit
368 int charsToClip = afterBegin - p->Handle.s;
369 p->Handle.s = this.Handle.s + this.Handle.lnth;
370 p->Handle.lnth -= charsToClip;
371 }
372 }
373 MoveThisAfter( p->Handle, pasting.Handle ); // move substring handle to maintain sorted order by string position
374 }
375}
376
377void ?=?(string_res &s, const char* other) {
378 assign(s, other, strlen(other));
379}
380
381void ?=?(string_res &s, char other) {
382 assign(s, &other, 1);
383}
384
385// Copy assignment operator
386void ?=?(string_res & this, const string_res & rhs) with( this ) {
387 assign(this, rhs.Handle.s, rhs.Handle.lnth);
388}
389
390void ?=?(string_res & this, string_res & rhs) with( this ) {
391 const string_res & rhs2 = rhs;
392 this = rhs2;
393}
394
395
396// Destructor
397void ^?{}(string_res &s) with(s) {
398 // much delegated to implied ^VbyteSM
399
400 // sever s from its share-edit peers, if any (four no-ops when already solo)
401 s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next;
402 s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev;
403 // s.shareEditSet_next = &s;
404 // s.shareEditSet_prev = &s;
405}
406
407
408// Returns the character at the given index
409// With unicode support, this may be different from just the byte at the given
410// offset from the start of the string.
411char ?[?](const string_res &s, size_t index) with(s) {
412 //TODO: Check if index is valid (no exceptions yet)
413 return Handle.s[index];
414}
415
416void assignAt(const string_res &s, size_t index, char val) {
417 string_res editZone = { s, SHARE_EDITS, index, index+1 };
418 assign(editZone, &val, 1);
419}
420
421
422///////////////////////////////////////////////////////////////////
423// Concatenation
424
425void append(string_res &str1, const char * buffer, size_t bsize) {
426 size_t clnth = size(str1) + bsize;
427 if ( str1.Handle.s + size(str1) == buffer ) { // already juxtapose ?
428 // no-op
429 } else { // must copy some text
430 if ( str1.Handle.s + size(str1) == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ?
431 VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area
432 } else { // copy the two parts
433 char * str1oldBuf = str1.Handle.s;
434 str1.Handle.s = VbyteAlloc( *str1.Handle.ulink, clnth );
435 ByteCopy( str1.Handle.s, 0, str1.Handle.lnth, str1oldBuf, 0, str1.Handle.lnth);
436 } // if
437 ByteCopy( str1.Handle.s, str1.Handle.lnth, bsize, (char*)buffer, 0, (int)bsize);
438 // VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth
439 } // if
440 str1.Handle.lnth = clnth;
441}
442
443void ?+=?(string_res &str1, const string_res &str2) {
444 append( str1, str2.Handle.s, str2.Handle.lnth );
445}
446
447void ?+=?(string_res &s, char other) {
448 append( s, &other, 1 );
449}
450
451void ?+=?(string_res &s, const char* other) {
452 append( s, other, strlen(other) );
453}
454
455
456
457
458//////////////////////////////////////////////////////////
459// Comparisons
460
461
462bool ?==?(const string_res &s1, const string_res &s2) {
463 return ByteCmp( s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;
464}
465
466bool ?!=?(const string_res &s1, const string_res &s2) {
467 return !(s1 == s2);
468}
469bool ?==?(const string_res &s, const char* other) {
470 string_res sother = other;
471 return s == sother;
472}
473bool ?!=?(const string_res &s, const char* other) {
474 return !(s == other);
475}
476
477
478//////////////////////////////////////////////////////////
479// Search
480
481bool contains(const string_res &s, char ch) {
482 for (i; size(s)) {
483 if (s[i] == ch) return true;
484 }
485 return false;
486}
487
488int find(const string_res &s, char search) {
489 for (i; size(s)) {
490 if (s[i] == search) return i;
491 }
492 return size(s);
493}
494
495 /* Remaining implementations essentially ported from Sunjay's work */
496
497int find(const string_res &s, const string_res &search) {
498 return find(s, search.Handle.s, search.Handle.lnth);
499}
500
501int find(const string_res &s, const char* search) {
502 return find(s, search, strlen(search));
503}
504
505int find(const string_res &s, const char* search, size_t searchsize) {
506 // FIXME: This is a naive algorithm. We probably want to switch to someting
507 // like Boyer-Moore in the future.
508 // https://en.wikipedia.org/wiki/String_searching_algorithm
509
510 // Always find the empty string
511 if (searchsize == 0) {
512 return 0;
513 }
514
515 for (size_t i = 0; i < s.Handle.lnth; i++) {
516 size_t remaining = s.Handle.lnth - i;
517 // Never going to find the search string if the remaining string is
518 // smaller than search
519 if (remaining < searchsize) {
520 break;
521 }
522
523 bool matched = true;
524 for (size_t j = 0; j < searchsize; j++) {
525 if (search[j] != s.Handle.s[i + j]) {
526 matched = false;
527 break;
528 }
529 }
530 if (matched) {
531 return i;
532 }
533 }
534
535 return s.Handle.lnth;
536}
537
538bool includes(const string_res &s, const string_res &search) {
539 return includes(s, search.Handle.s, search.Handle.lnth);
540}
541
542bool includes(const string_res &s, const char* search) {
543 return includes(s, search, strlen(search));
544}
545
546bool includes(const string_res &s, const char* search, size_t searchsize) {
547 return find(s, search, searchsize) < s.Handle.lnth;
548}
549
550bool startsWith(const string_res &s, const string_res &prefix) {
551 return startsWith(s, prefix.Handle.s, prefix.Handle.lnth);
552}
553
554bool startsWith(const string_res &s, const char* prefix) {
555 return startsWith(s, prefix, strlen(prefix));
556}
557
558bool startsWith(const string_res &s, const char* prefix, size_t prefixsize) {
559 if (s.Handle.lnth < prefixsize) {
560 return false;
561 }
562 return memcmp(s.Handle.s, prefix, prefixsize) == 0;
563}
564
565bool endsWith(const string_res &s, const string_res &suffix) {
566 return endsWith(s, suffix.Handle.s, suffix.Handle.lnth);
567}
568
569bool endsWith(const string_res &s, const char* suffix) {
570 return endsWith(s, suffix, strlen(suffix));
571}
572
573bool endsWith(const string_res &s, const char* suffix, size_t suffixsize) {
574 if (s.Handle.lnth < suffixsize) {
575 return false;
576 }
577 // Amount to offset the bytes pointer so that we are comparing the end of s
578 // to suffix. s.bytes + offset should be the first byte to compare against suffix
579 size_t offset = s.Handle.lnth - suffixsize;
580 return memcmp(s.Handle.s + offset, suffix, suffixsize) == 0;
581}
582
583 /* Back to Mike's work */
584
585
586///////////////////////////////////////////////////////////////////////////
587// charclass, include, exclude
588
589void ?{}( charclass_res & this, const string_res & chars) {
590 (this){ chars.Handle.s, chars.Handle.lnth };
591}
592
593void ?{}( charclass_res & this, const char * chars ) {
594 (this){ chars, strlen(chars) };
595}
596
597void ?{}( charclass_res & this, const char * chars, size_t charssize ) {
598 (this.chars){ chars, charssize };
599 // now sort it ?
600}
601
602void ^?{}( charclass_res & this ) {
603 ^(this.chars){};
604}
605
606static bool test( const charclass_res & mask, char c ) {
607 // instead, use sorted char list?
608 return contains( mask.chars, c );
609}
610
611int exclude(const string_res &s, const charclass_res &mask) {
612 for (int i = 0; i < size(s); i++) {
613 if ( test(mask, s[i]) ) return i;
614 }
615 return size(s);
616}
617
618int include(const string_res &s, const charclass_res &mask) {
619 for (int i = 0; i < size(s); i++) {
620 if ( ! test(mask, s[i]) ) return i;
621 }
622 return size(s);
623}
624
625//######################### VbyteHeap "implementation" #########################
626
627
628// Add a new HandleNode node n after the current HandleNode node.
629
630static inline void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {
631#ifdef VbyteDebug
632 serr | "enter:AddThisAfter, this:" | &this | " n:" | &n;
633#endif // VbyteDebug
634 verify( n.ulink != 0p );
635 verify( this.ulink == n.ulink );
636 flink = n.flink;
637 blink = &n;
638 n.flink->blink = &this;
639 n.flink = &this;
640#ifdef VbyteDebug
641 {
642 serr | "HandleList:";
643 serr | nlOff;
644 for ( HandleNode *ni = HeaderPtr->flink; ni != HeaderPtr; ni = ni->flink ) {
645 serr | "\tnode:" | ni | " lnth:" | ni->lnth | " s:" | (void *)ni->s | ",\"";
646 for ( int i = 0; i < ni->lnth; i += 1 ) {
647 serr | ni->s[i];
648 } // for
649 serr | "\" flink:" | ni->flink | " blink:" | ni->blink | nl;
650 } // for
651 serr | nlOn;
652 }
653 serr | "exit:AddThisAfter";
654#endif // VbyteDebug
655} // AddThisAfter
656
657
658// Delete the current HandleNode node.
659
660static inline void DeleteNode( HandleNode & this ) with(this) {
661#ifdef VbyteDebug
662 serr | "enter:DeleteNode, this:" | &this;
663#endif // VbyteDebug
664 flink->blink = blink;
665 blink->flink = flink;
666#ifdef VbyteDebug
667 serr | "exit:DeleteNode";
668#endif // VbyteDebug
669} // DeleteNode
670
671
672
673// Allocates specified storage for a string from byte-string area. If not enough space remains to perform the
674// allocation, the garbage collection routine is called and a second attempt is made to allocate the space. If the
675// second attempt fails, a further attempt is made to create a new, larger byte-string area.
676
677static inline char * VbyteAlloc( VbyteHeap & this, int size ) with(this) {
678#ifdef VbyteDebug
679 serr | "enter:VbyteAlloc, size:" | size;
680#endif // VbyteDebug
681 uintptr_t NoBytes;
682 char *r;
683
684 NoBytes = ( uintptr_t )EndVbyte + size;
685 if ( NoBytes > ( uintptr_t )ExtVbyte ) { // enough room for new byte-string ?
686 garbage( this ); // firer up the garbage collector
687 NoBytes = ( uintptr_t )EndVbyte + size; // try again
688 if ( NoBytes > ( uintptr_t )ExtVbyte ) { // enough room for new byte-string ?
689// unimplemented feature - assert, not verify
690assert( 0 && "need to implement actual growth" );
691 // extend( size ); // extend the byte-string area
692 } // if
693 } // if
694 r = EndVbyte;
695 EndVbyte += size;
696#ifdef VbyteDebug
697 serr | "exit:VbyteAlloc, r:" | (void *)r | " EndVbyte:" | (void *)EndVbyte | " ExtVbyte:" | ExtVbyte;
698#endif // VbyteDebug
699 return r;
700} // VbyteAlloc
701
702
703// Move an existing HandleNode node h somewhere after the current HandleNode node so that it is in ascending order by
704// the address in the byte string area.
705
706static inline void MoveThisAfter( HandleNode & this, const HandleNode & h ) with(this) {
707#ifdef VbyteDebug
708 serr | "enter:MoveThisAfter, this:" | & this | " h:" | & h;
709#endif // VbyteDebug
710 verify( h.ulink != 0p );
711 verify( this.ulink == h.ulink );
712 if ( s < h.s ) { // check argument values
713 // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:"
714 // | ( h->s ) | " and keep handles in ascending order";
715 // exit(-1 );
716 verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");
717 } // if
718
719 HandleNode *i;
720 for ( i = h.flink; i->s != 0 && s > ( i->s ); i = i->flink ); // find the position for this node after h
721 if ( & this != i->blink ) {
722 DeleteNode( this );
723 AddThisAfter( this, *i->blink );
724 } // if
725#ifdef VbyteDebug
726 {
727 serr | "HandleList:";
728 serr | nlOff;
729 for ( HandleNode *n = HeaderPtr->flink; n != HeaderPtr; n = n->flink ) {
730 serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
731 for ( int i = 0; i < n->lnth; i += 1 ) {
732 serr | n->s[i];
733 } // for
734 serr | "\" flink:" | n->flink | " blink:" | n->blink | nl;
735 } // for
736 serr | nlOn;
737 }
738 serr | "exit:MoveThisAfter";
739#endif // VbyteDebug
740} // MoveThisAfter
741
742
743
744
745
746//######################### VbyteHeap #########################
747
748// Move characters from one location in the byte-string area to another. The routine handles the following situations:
749//
750// if the |Src| > |Dst| => truncate
751// if the |Dst| > |Src| => pad Dst with blanks
752
753void ByteCopy( char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth ) {
754 for ( int i = 0; i < DstLnth; i += 1 ) {
755 if ( i == SrcLnth ) { // |Dst| > |Src|
756 for ( ; i < DstLnth; i += 1 ) { // pad Dst with blanks
757 Dst[DstStart + i] = ' ';
758 } // for
759 break;
760 } // exit
761 Dst[DstStart + i] = Src[SrcStart + i];
762 } // for
763} // ByteCopy
764
765// Compare two byte strings in the byte-string area. The routine returns the following values:
766//
767// 1 => Src1-byte-string > Src2-byte-string
768// 0 => Src1-byte-string = Src2-byte-string
769// -1 => Src1-byte-string < Src2-byte-string
770
771int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) {
772#ifdef VbyteDebug
773 serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth;
774#endif // VbyteDebug
775 int cmp;
776
777 CharZip: for ( int i = 0; ; i += 1 ) {
778 if ( i == Src2Lnth - 1 ) {
779 for ( ; ; i += 1 ) {
780 if ( i == Src1Lnth - 1 ) {
781 cmp = 0;
782 break CharZip;
783 } // exit
784 if ( Src1[Src1Start + i] != ' ') {
785 // SUSPECTED BUG: this could be be why Peter got the bug report about == " " (why is this case here at all?)
786 cmp = 1;
787 break CharZip;
788 } // exit
789 } // for
790 } // exit
791 if ( i == Src1Lnth - 1 ) {
792 for ( ; ; i += 1 ) {
793 if ( i == Src2Lnth - 1 ) {
794 cmp = 0;
795 break CharZip;
796 } // exit
797 if ( Src2[Src2Start + i] != ' ') {
798 cmp = -1;
799 break CharZip;
800 } // exit
801 } // for
802 } // exit
803 if ( Src2[Src2Start + i] != Src1[Src1Start+ i]) {
804 cmp = Src1[Src1Start + i] > Src2[Src2Start + i] ? 1 : -1;
805 break CharZip;
806 } // exit
807 } // for
808#ifdef VbyteDebug
809 serr | "exit:ByteCmp, cmp:" | cmp;
810#endif // VbyteDebug
811 return cmp;
812} // ByteCmp
813
814
815// The compaction moves all of the byte strings currently in use to the beginning of the byte-string area and modifies
816// the handles to reflect the new positions of the byte strings. Compaction assumes that the handle list is in ascending
817// order by pointers into the byte-string area. The strings associated with substrings do not have to be moved because
818// the containing string has been moved. Hence, they only require that their string pointers be adjusted.
819
820void compaction(VbyteHeap & this) with(this) {
821 HandleNode *h;
822 char *obase, *nbase, *limit;
823
824 NoOfCompactions += 1;
825 EndVbyte = StartVbyte;
826 h = Header.flink; // ignore header node
827 for (;;) {
828 ByteCopy( EndVbyte, 0, h->lnth, h->s, 0, h->lnth );
829 obase = h->s;
830 h->s = EndVbyte;
831 nbase = h->s;
832 EndVbyte += h->lnth;
833 limit = obase + h->lnth;
834 h = h->flink;
835
836 // check if any substrings are allocated within a string
837
838 for (;;) {
839 if ( h == &Header ) break; // end of header list ?
840 if ( h->s >= limit ) break; // outside of current string ?
841 h->s = nbase + (( uintptr_t )h->s - ( uintptr_t )obase );
842 h = h->flink;
843 } // for
844 if ( h == &Header ) break; // end of header list ?
845 } // for
846} // compaction
847
848
849// Garbage determines the amount of free space left in the heap and then reduces, leave the same, or extends the size of
850// the heap. The heap is then compacted in the existing heap or into the newly allocated heap.
851
852void garbage(VbyteHeap & this ) with(this) {
853#ifdef VbyteDebug
854 serr | "enter:garbage";
855 {
856 serr | "HandleList:";
857 for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
858 serr | nlOff;
859 serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
860 for ( int i = 0; i < n->lnth; i += 1 ) {
861 serr | n->s[i];
862 } // for
863 serr | nlOn;
864 serr | "\" flink:" | n->flink | " blink:" | n->blink;
865 } // for
866 }
867#endif // VbyteDebug
868 int AmountUsed, AmountFree;
869
870 AmountUsed = 0;
871 for ( HandleNode *i = Header.flink; i != &Header; i = i->flink ) { // calculate amount of byte area used
872 AmountUsed += i->lnth;
873 } // for
874 AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed;
875
876 if ( AmountFree < ( int )( CurrSize * 0.1 )) { // free space less than 10% ?
877
878// unimplemented feature - assert, not verify
879assert( 0 && "need to implement actual growth" );
880// extend( CurrSize ); // extend the heap
881
882 // Peter says, "This needs work before it should be used."
883 // } else if ( AmountFree > CurrSize / 2 ) { // free space greater than 3 times the initial allocation ?
884 // reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory
885
886 } // if
887 compaction(this); // compact the byte area, in the same or new heap area
888#ifdef VbyteDebug
889 {
890 serr | "HandleList:";
891 for ( HandleNode *n = Header.flink; n != &Header; n = n->flink ) {
892 serr | nlOff;
893 serr | "\tnode:" | n | " lnth:" | n->lnth | " s:" | (void *)n->s | ",\"";
894 for ( int i = 0; i < n->lnth; i += 1 ) {
895 serr | n->s[i];
896 } // for
897 serr | nlOn;
898 serr | "\" flink:" | n->flink | " blink:" | n->blink;
899 } // for
900 }
901 serr | "exit:garbage";
902#endif // VbyteDebug
903} // garbage
904
905#undef VbyteDebug
906
907//WIP
908#if 0
909
910
911// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
912// area is deleted.
913
914void VbyteHeap::extend( int size ) {
915#ifdef VbyteDebug
916 serr | "enter:extend, size:" | size;
917#endif // VbyteDebug
918 char *OldStartVbyte;
919
920 NoOfExtensions += 1;
921 OldStartVbyte = StartVbyte; // save previous byte area
922
923 CurrSize += size > InitSize ? size : InitSize; // minimum extension, initial size
924 StartVbyte = EndVbyte = new char[CurrSize];
925 ExtVbyte = (void *)( StartVbyte + CurrSize );
926 compaction(); // copy from old heap to new & adjust pointers to new heap
927 delete OldStartVbyte; // release old heap
928#ifdef VbyteDebug
929 serr | "exit:extend, CurrSize:" | CurrSize;
930#endif // VbyteDebug
931} // extend
932
933
934// Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
935// area is deleted.
936
937void VbyteHeap::reduce( int size ) {
938#ifdef VbyteDebug
939 serr | "enter:reduce, size:" | size;
940#endif // VbyteDebug
941 char *OldStartVbyte;
942
943 NoOfReductions += 1;
944 OldStartVbyte = StartVbyte; // save previous byte area
945
946 CurrSize -= size;
947 StartVbyte = EndVbyte = new char[CurrSize];
948 ExtVbyte = (void *)( StartVbyte + CurrSize );
949 compaction(); // copy from old heap to new & adjust pointers to new heap
950 delete OldStartVbyte; // release old heap
951#ifdef VbyteDebug
952 serr | "exit:reduce, CurrSize:" | CurrSize;
953#endif // VbyteDebug
954} // reduce
955
956
957#endif
Note: See TracBrowser for help on using the repository browser.