Changeset d672350 for libcfa/src/containers/string_res.cfa
- Timestamp:
- Mar 21, 2022, 1:44:06 PM (4 years ago)
- Branches:
- ADT, ast-experimental, enum, master, pthread-emulation, qualifiedEnum
- Children:
- a76202d
- Parents:
- ef3c383 (diff), dbe2533 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/containers/string_res.cfa
ref3c383 rd672350 15 15 16 16 #include "string_res.hfa" 17 #include <stdlib.hfa> // e.g. malloc 18 #include <string.h> // e.g. strlen 17 #include "string_sharectx.hfa" 18 #include "stdlib.hfa" 19 20 // Workaround for observed performance penalty from calling CFA's alloc. 21 // Workaround is: EndVbyte = TEMP_ALLOC(char, CurrSize) 22 // Should be: EndVbyte = alloc(CurrSize) 23 #define TEMP_ALLOC(T, n) (( T* ) malloc( n * sizeof( T ) )) 24 25 #include <assert.h> 19 26 20 27 //######################### VbyteHeap "header" ######################### 21 22 23 24 25 26 27 28 29 // DON'T COMMIT:30 // #define VbyteDebug31 32 33 34 35 28 36 29 #ifdef VbyteDebug … … 54 47 55 48 56 static inlinevoid compaction( VbyteHeap & ); // compaction of the byte area57 static inline void garbage( VbyteHeap &); // garbage collect the byte area58 static inlinevoid extend( VbyteHeap &, int ); // extend the size of the byte area59 static inlinevoid reduce( VbyteHeap &, int ); // reduce the size of the byte area60 61 static inline void ?{}( VbyteHeap &, int = 1000 );62 static inlinevoid ^?{}( VbyteHeap & );63 static inline void ByteCopy( VbyteHeap &, char *, int, int, char *, int, int ); // copy a block of bytes from one location in the heap to another 64 static in line int ByteCmp( VbyteHeap &,char *, int, int, char *, int, int ); // compare 2 blocks of bytes65 static inlinechar *VbyteAlloc( VbyteHeap &, int ); // allocate a block bytes in the heap66 67 68 static inlinevoid AddThisAfter( HandleNode &, HandleNode & );69 static inlinevoid DeleteNode( HandleNode & );70 static inlinevoid MoveThisAfter( HandleNode &, const HandleNode & ); // move current handle after parameter handle49 static void compaction( VbyteHeap & ); // compaction of the byte area 50 static void garbage( VbyteHeap &, int ); // garbage collect the byte area 51 static void extend( VbyteHeap &, int ); // extend the size of the byte area 52 static void reduce( VbyteHeap &, int ); // reduce the size of the byte area 53 54 static void ?{}( VbyteHeap &, size_t = 1000 ); 55 static void ^?{}( VbyteHeap & ); 56 57 static int ByteCmp( char *, int, int, char *, int, int ); // compare 2 blocks of bytes 58 static char *VbyteAlloc( VbyteHeap &, int ); // allocate a block bytes in the heap 59 static char *VbyteTryAdjustLast( VbyteHeap &, int ); 60 61 static void AddThisAfter( HandleNode &, HandleNode & ); 62 static void DeleteNode( HandleNode & ); 63 static void MoveThisAfter( HandleNode &, const HandleNode & ); // move current handle after parameter handle 71 64 72 65 73 66 // Allocate the storage for the variable sized area and intialize the heap variables. 74 67 75 static inline void ?{}( VbyteHeap & this, int Size ) with(this) {68 static void ?{}( VbyteHeap & this, size_t Size ) with(this) { 76 69 #ifdef VbyteDebug 77 70 serr | "enter:VbyteHeap::VbyteHeap, this:" | &this | " Size:" | Size; … … 79 72 NoOfCompactions = NoOfExtensions = NoOfReductions = 0; 80 73 InitSize = CurrSize = Size; 81 StartVbyte = EndVbyte = alloc(CurrSize);74 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize); 82 75 ExtVbyte = (void *)( StartVbyte + CurrSize ); 83 76 Header.flink = Header.blink = &Header; 77 Header.ulink = & this; 84 78 #ifdef VbyteDebug 85 79 HeaderPtr = &Header; … … 91 85 // Release the dynamically allocated storage for the byte area. 92 86 93 static inlinevoid ^?{}( VbyteHeap & this ) with(this) {87 static void ^?{}( VbyteHeap & this ) with(this) { 94 88 free( StartVbyte ); 95 89 } // ~VbyteHeap … … 102 96 // creator. 103 97 104 void ?{}( HandleNode & this ) with(this) {98 static void ?{}( HandleNode & this ) with(this) { 105 99 #ifdef VbyteDebug 106 100 serr | "enter:HandleNode::HandleNode, this:" | &this; … … 117 111 // collection. 118 112 119 void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) {113 static void ?{}( HandleNode & this, VbyteHeap & vh ) with(this) { 120 114 #ifdef VbyteDebug 121 115 serr | "enter:HandleNode::HandleNode, this:" | &this; … … 123 117 s = 0; 124 118 lnth = 0; 119 ulink = &vh; 125 120 AddThisAfter( this, *vh.Header.blink ); 126 121 #ifdef VbyteDebug … … 133 128 // is the responsibility of the creator to destroy it. 134 129 135 void ^?{}( HandleNode & this ) with(this) {130 static void ^?{}( HandleNode & this ) with(this) { 136 131 #ifdef VbyteDebug 137 132 serr | "enter:HandleNode::~HandleNode, this:" | & this; … … 149 144 } // ~HandleNode 150 145 146 147 //######################### String Sharing Context ######################### 148 149 static string_sharectx * ambient_string_sharectx; // fickle top of stack 150 static string_sharectx default_string_sharectx = {NEW_SHARING}; // stable bottom of stack 151 152 void ?{}( string_sharectx & this, StringSharectx_Mode mode ) with( this ) { 153 (older){ ambient_string_sharectx }; 154 if ( mode == NEW_SHARING ) { 155 (activeHeap){ new( (size_t) 1000 ) }; 156 } else { 157 verify( mode == NO_SHARING ); 158 (activeHeap){ 0p }; 159 } 160 ambient_string_sharectx = & this; 161 } 162 163 void ^?{}( string_sharectx & this ) with( this ) { 164 if ( activeHeap ) delete( activeHeap ); 165 166 // unlink this from older-list starting from ambient_string_sharectx 167 // usually, this==ambient_string_sharectx and the loop runs zero times 168 string_sharectx *& c = ambient_string_sharectx; 169 while ( c != &this ) &c = &c->older; // find this 170 c = this.older; // unlink 171 } 172 151 173 //######################### String Resource ######################### 152 174 153 175 154 VbyteHeap HeapArea; 155 156 VbyteHeap * DEBUG_string_heap = & HeapArea; 176 VbyteHeap * DEBUG_string_heap() { 177 assert( ambient_string_sharectx->activeHeap && "No sharing context is active" ); 178 return ambient_string_sharectx->activeHeap; 179 } 157 180 158 181 size_t DEBUG_string_bytes_avail_until_gc( VbyteHeap * heap ) { … … 160 183 } 161 184 185 size_t DEBUG_string_bytes_in_heap( VbyteHeap * heap ) { 186 return heap->CurrSize; 187 } 188 162 189 const char * DEBUG_string_heap_start( VbyteHeap * heap ) { 163 190 return heap->StartVbyte; 164 191 } 165 166 192 167 193 // Returns the size of the string in bytes … … 187 213 // Store auto-newline state so it can be restored 188 214 bool anl = getANL$(out); 189 nlOff(out); 190 for (size_t i = 0; i < s.Handle.lnth; i++) { 191 // Need to re-apply on the last output operator, for whole-statement version 192 if (anl && i == s.Handle.lnth-1) nlOn(out); 193 out | s[i]; 194 } 195 return out; 215 if( s.Handle.lnth == 0 ) { 216 sout | ""; 217 } else { 218 nlOff(out); 219 for (size_t i = 0; i < s.Handle.lnth; i++) { 220 // Need to re-apply on the last output operator, for whole-statement version 221 if (anl && i == s.Handle.lnth-1) nlOn(out); 222 out | s[i]; 223 } 224 } 196 225 } 197 226 198 227 // Empty constructor 199 228 void ?{}(string_res &s) with(s) { 200 (Handle){ HeapArea }; 229 if( ambient_string_sharectx->activeHeap ) { 230 (Handle){ * ambient_string_sharectx->activeHeap }; 231 (shareEditSet_owns_ulink){ false }; 232 verify( Handle.s == 0p && Handle.lnth == 0 ); 233 } else { 234 (Handle){ * new( (size_t) 10 ) }; // TODO: can I lazily avoid allocating for empty string 235 (shareEditSet_owns_ulink){ true }; 236 Handle.s = Handle.ulink->StartVbyte; 237 verify( Handle.lnth == 0 ); 238 } 201 239 s.shareEditSet_prev = &s; 202 240 s.shareEditSet_next = &s; 203 241 } 204 242 243 static void eagerCopyCtorHelper(string_res &s, const char* rhs, size_t rhslnth) with(s) { 244 if( ambient_string_sharectx->activeHeap ) { 245 (Handle){ * ambient_string_sharectx->activeHeap }; 246 (shareEditSet_owns_ulink){ false }; 247 } else { 248 (Handle){ * new( rhslnth ) }; 249 (shareEditSet_owns_ulink){ true }; 250 } 251 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth); 252 Handle.lnth = rhslnth; 253 memmove( Handle.s, rhs, rhslnth ); 254 s.shareEditSet_prev = &s; 255 s.shareEditSet_next = &s; 256 } 257 205 258 // Constructor from a raw buffer and size 206 259 void ?{}(string_res &s, const char* rhs, size_t rhslnth) with(s) { 207 (Handle){ HeapArea }; 208 Handle.s = VbyteAlloc(HeapArea, rhslnth); 260 eagerCopyCtorHelper(s, rhs, rhslnth); 261 } 262 263 // private ctor (not in header): use specified heap (ignore ambient) and copy chars in 264 void ?{}( string_res &s, VbyteHeap & heap, const char* rhs, size_t rhslnth ) with(s) { 265 (Handle){ heap }; 266 Handle.s = VbyteAlloc(*Handle.ulink, rhslnth); 209 267 Handle.lnth = rhslnth; 210 for ( int i = 0; i < rhslnth; i += 1 ) { // copy characters 211 Handle.s[i] = rhs[i]; 212 } // for 268 (s.shareEditSet_owns_ulink){ false }; 269 memmove( Handle.s, rhs, rhslnth ); 213 270 s.shareEditSet_prev = &s; 214 271 s.shareEditSet_next = &s; 215 272 } 216 273 217 // String literal constructor218 void ?{}(string_res &s, const char* rhs) {219 (s){ rhs, strlen(rhs) };220 }221 222 274 // General copy constructor 223 275 void ?{}(string_res &s, const string_res & s2, StrResInitMode mode, size_t start, size_t end ) { 224 276 225 (s.Handle){ HeapArea }; 226 s.Handle.s = s2.Handle.s + start; 227 s.Handle.lnth = end - start; 228 MoveThisAfter(s.Handle, s2.Handle ); // insert this handle after rhs handle 229 // ^ bug? skip others at early point in string 230 231 if (mode == COPY_VALUE) { 232 // make s alone in its shareEditSet 233 s.shareEditSet_prev = &s; 234 s.shareEditSet_next = &s; 277 verify( start <= end && end <= s2.Handle.lnth ); 278 279 if (s2.Handle.ulink != ambient_string_sharectx->activeHeap && mode == COPY_VALUE) { 280 // crossing heaps (including private): copy eagerly 281 eagerCopyCtorHelper(s, s2.Handle.s + start, end - start); 282 verify(s.shareEditSet_prev == &s); 283 verify(s.shareEditSet_next == &s); 235 284 } else { 236 assert( mode == SHARE_EDITS ); 237 238 // s2 is logically const but not implementation const 239 string_res & s2mod = (string_res &) s2; 240 241 // insert s after s2 on shareEditSet 242 s.shareEditSet_next = s2mod.shareEditSet_next; 243 s.shareEditSet_prev = &s2mod; 244 s.shareEditSet_next->shareEditSet_prev = &s; 245 s.shareEditSet_prev->shareEditSet_next = &s; 246 } 247 } 248 249 void assign(string_res &this, const char* buffer, size_t bsize) { 250 251 // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs 252 string_res * shareEditSetStartPeer = & this; 253 string_res * shareEditSetEndPeer = & this; 254 for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) { 255 if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) { 256 shareEditSetStartPeer = editPeer; 285 (s.Handle){}; 286 s.Handle.s = s2.Handle.s + start; 287 s.Handle.lnth = end - start; 288 s.Handle.ulink = s2.Handle.ulink; 289 290 AddThisAfter(s.Handle, s2.Handle ); // insert this handle after rhs handle 291 // ^ bug? skip others at early point in string 292 293 if (mode == COPY_VALUE) { 294 verify(s2.Handle.ulink == ambient_string_sharectx->activeHeap); 295 // requested logical copy in same heap: defer copy until write 296 297 (s.shareEditSet_owns_ulink){ false }; 298 299 // make s alone in its shareEditSet 300 s.shareEditSet_prev = &s; 301 s.shareEditSet_next = &s; 302 } else { 303 verify( mode == SHARE_EDITS ); 304 // sharing edits with source forces same heap as source (ignore context) 305 306 (s.shareEditSet_owns_ulink){ s2.shareEditSet_owns_ulink }; 307 308 // s2 is logically const but not implementation const 309 string_res & s2mod = (string_res &) s2; 310 311 // insert s after s2 on shareEditSet 312 s.shareEditSet_next = s2mod.shareEditSet_next; 313 s.shareEditSet_prev = &s2mod; 314 s.shareEditSet_next->shareEditSet_prev = &s; 315 s.shareEditSet_prev->shareEditSet_next = &s; 257 316 } 258 if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) { 259 shareEditSetEndPeer = editPeer; 260 } 261 } 262 263 // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer 264 // `this` occurs in the middle of it, to be replaced 265 // build up the new text in `pasting` 266 267 string_res pasting = { 268 shareEditSetStartPeer->Handle.s, // start of SES 269 this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this 270 append( pasting, 271 buffer, // start of replacement for this 272 bsize ); // length of replacement for this 273 append( pasting, 274 this.Handle.s + this.Handle.lnth, // start of SES after this 275 shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - 276 (this.Handle.s + this.Handle.lnth) ); // length of SES, after this 277 278 // The above string building can trigger compaction. 279 // The reference points (that are arguments of the string building) may move during that building. 280 // From this point on, they are stable. 281 // So now, capture their values for use in the overlap cases, below. 282 // Do not factor these definitions with the arguments used above. 317 } 318 } 319 320 static void assignEditSet(string_res & this, string_res * shareEditSetStartPeer, string_res * shareEditSetEndPeer, 321 char * resultSesStart, 322 size_t resultSesLnth, 323 HandleNode * resultPadPosition, size_t bsize ) { 283 324 284 325 char * beforeBegin = shareEditSetStartPeer->Handle.s; … … 290 331 size_t oldLnth = this.Handle.lnth; 291 332 292 this.Handle.s = pasting.Handle.s+ beforeLen;333 this.Handle.s = resultSesStart + beforeLen; 293 334 this.Handle.lnth = bsize; 294 MoveThisAfter( this.Handle, pasting.Handle ); 335 if (resultPadPosition) 336 MoveThisAfter( this.Handle, *resultPadPosition ); 295 337 296 338 // adjust all substring string and handle locations, and check if any substring strings are outside the new base string 297 char *limit = pasting.Handle.s + pasting.Handle.lnth;339 char *limit = resultSesStart + resultSesLnth; 298 340 for (string_res * p = this.shareEditSet_next; p != &this; p = p->shareEditSet_next) { 299 assert(p->Handle.s >= beforeBegin);341 verify (p->Handle.s >= beforeBegin); 300 342 if ( p->Handle.s >= afterBegin ) { 301 assert( p->Handle.s <= afterBegin + afterLen );302 assert( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );343 verify ( p->Handle.s <= afterBegin + afterLen ); 344 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen ); 303 345 // p starts after the edit 304 346 // take start and end as end-anchored … … 318 360 } else { 319 361 // p ends after the edit 320 assert( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen );362 verify ( p->Handle.s + p->Handle.lnth <= afterBegin + afterLen ); 321 363 // take end as end-anchored 322 364 // stretch-shrink p according to the edit … … 326 368 // take start as start-anchored 327 369 size_t startOffsetFromStart = p->Handle.s - beforeBegin; 328 p->Handle.s = pasting.Handle.s+ startOffsetFromStart;370 p->Handle.s = resultSesStart + startOffsetFromStart; 329 371 } else { 330 assert( p->Handle.s < afterBegin );372 verify ( p->Handle.s < afterBegin ); 331 373 // p starts during the edit 332 assert( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen );374 verify( p->Handle.s + p->Handle.lnth >= beforeBegin + beforeLen ); 333 375 if ( p->Handle.s + p->Handle.lnth < afterBegin ) { 334 376 // p ends during the edit; p does not include the last character replaced … … 344 386 } 345 387 } 346 MoveThisAfter( p->Handle, pasting.Handle ); // move substring handle to maintain sorted order by string position 347 } 348 } 349 350 void ?=?(string_res &s, const char* other) { 351 assign(s, other, strlen(other)); 352 } 353 354 void ?=?(string_res &s, char other) { 355 assign(s, &other, 1); 388 if (resultPadPosition) 389 MoveThisAfter( p->Handle, *resultPadPosition ); // move substring handle to maintain sorted order by string position 390 } 391 } 392 393 static string_res & assign_(string_res &this, const char* buffer, size_t bsize, const string_res & valSrc) { 394 395 // traverse the incumbent share-edit set (SES) to recover the range of a base string to which `this` belongs 396 string_res * shareEditSetStartPeer = & this; 397 string_res * shareEditSetEndPeer = & this; 398 for (string_res * editPeer = this.shareEditSet_next; editPeer != &this; editPeer = editPeer->shareEditSet_next) { 399 if ( editPeer->Handle.s < shareEditSetStartPeer->Handle.s ) { 400 shareEditSetStartPeer = editPeer; 401 } 402 if ( shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth < editPeer->Handle.s + editPeer->Handle.lnth) { 403 shareEditSetEndPeer = editPeer; 404 } 405 } 406 407 verify( shareEditSetEndPeer->Handle.s >= shareEditSetStartPeer->Handle.s ); 408 size_t origEditSetLength = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - shareEditSetStartPeer->Handle.s; 409 verify( origEditSetLength >= this.Handle.lnth ); 410 411 if ( this.shareEditSet_owns_ulink ) { // assigning to private context 412 // ok to overwrite old value within LHS 413 char * prefixStartOrig = shareEditSetStartPeer->Handle.s; 414 int prefixLen = this.Handle.s - prefixStartOrig; 415 char * suffixStartOrig = this.Handle.s + this.Handle.lnth; 416 int suffixLen = shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - suffixStartOrig; 417 418 int delta = bsize - this.Handle.lnth; 419 if ( char * oldBytes = VbyteTryAdjustLast( *this.Handle.ulink, delta ) ) { 420 // growing: copy from old to new 421 char * dest = VbyteAlloc( *this.Handle.ulink, origEditSetLength + delta ); 422 char *destCursor = dest; memcpy(destCursor, prefixStartOrig, prefixLen); 423 destCursor += prefixLen; memcpy(destCursor, buffer , bsize ); 424 destCursor += bsize; memcpy(destCursor, suffixStartOrig, suffixLen); 425 assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer, 426 dest, 427 origEditSetLength + delta, 428 0p, bsize); 429 free( oldBytes ); 430 } else { 431 // room is already allocated in-place: bubble suffix and overwite middle 432 memmove( suffixStartOrig + delta, suffixStartOrig, suffixLen ); 433 memcpy( this.Handle.s, buffer, bsize ); 434 435 assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer, 436 shareEditSetStartPeer->Handle.s, 437 origEditSetLength + delta, 438 0p, bsize); 439 } 440 441 } else if ( // assigning to shared context 442 this.Handle.lnth == origEditSetLength && // overwriting entire run of SES 443 & valSrc && // sourcing from a managed string 444 valSrc.Handle.ulink == this.Handle.ulink ) { // sourcing from same heap 445 446 // SES's result will only use characters from the source string => reuse source 447 assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer, 448 valSrc.Handle.s, 449 valSrc.Handle.lnth, 450 &((string_res&)valSrc).Handle, bsize); 451 452 } else { 453 // overwriting a proper substring of some string: mash characters from old and new together (copy on write) 454 // OR we are importing characters: need to copy eagerly (can't refer to source) 455 456 // full string is from start of shareEditSetStartPeer thru end of shareEditSetEndPeer 457 // `this` occurs in the middle of it, to be replaced 458 // build up the new text in `pasting` 459 460 string_res pasting = { 461 * this.Handle.ulink, // maintain same heap, regardless of context 462 shareEditSetStartPeer->Handle.s, // start of SES 463 this.Handle.s - shareEditSetStartPeer->Handle.s }; // length of SES, before this 464 append( pasting, 465 buffer, // start of replacement for this 466 bsize ); // length of replacement for this 467 append( pasting, 468 this.Handle.s + this.Handle.lnth, // start of SES after this 469 shareEditSetEndPeer->Handle.s + shareEditSetEndPeer->Handle.lnth - 470 (this.Handle.s + this.Handle.lnth) ); // length of SES, after this 471 472 // The above string building can trigger compaction. 473 // The reference points (that are arguments of the string building) may move during that building. 474 // From this point on, they are stable. 475 476 assignEditSet(this, shareEditSetStartPeer, shareEditSetEndPeer, 477 pasting.Handle.s, 478 pasting.Handle.lnth, 479 &pasting.Handle, bsize); 480 } 481 482 return this; 483 } 484 485 string_res & assign(string_res &this, const char* buffer, size_t bsize) { 486 return assign_(this, buffer, bsize, *0p); 487 } 488 489 string_res & ?=?(string_res &s, char other) { 490 return assign(s, &other, 1); 356 491 } 357 492 358 493 // Copy assignment operator 359 void?=?(string_res & this, const string_res & rhs) with( this ) {360 assign(this, rhs.Handle.s, rhs.Handle.lnth);361 } 362 363 void?=?(string_res & this, string_res & rhs) with( this ) {494 string_res & ?=?(string_res & this, const string_res & rhs) with( this ) { 495 return assign_(this, rhs.Handle.s, rhs.Handle.lnth, rhs); 496 } 497 498 string_res & ?=?(string_res & this, string_res & rhs) with( this ) { 364 499 const string_res & rhs2 = rhs; 365 this = rhs2;500 return this = rhs2; 366 501 } 367 502 … … 374 509 s.shareEditSet_prev->shareEditSet_next = s.shareEditSet_next; 375 510 s.shareEditSet_next->shareEditSet_prev = s.shareEditSet_prev; 376 s.shareEditSet_next = &s; 377 s.shareEditSet_prev = &s; 511 // s.shareEditSet_next = &s; 512 // s.shareEditSet_prev = &s; 513 514 if (shareEditSet_owns_ulink && s.shareEditSet_next == &s) { // last one out 515 delete( s.Handle.ulink ); 516 } 378 517 } 379 518 … … 387 526 } 388 527 528 void assignAt(const string_res &s, size_t index, char val) { 529 string_res editZone = { s, SHARE_EDITS, index, index+1 }; 530 assign(editZone, &val, 1); 531 } 532 389 533 390 534 /////////////////////////////////////////////////////////////////// … … 392 536 393 537 void append(string_res &str1, const char * buffer, size_t bsize) { 394 size_t clnth = s ize(str1)+ bsize;395 if ( str1.Handle.s + s ize(str1)== buffer ) { // already juxtapose ?538 size_t clnth = str1.Handle.lnth + bsize; 539 if ( str1.Handle.s + str1.Handle.lnth == buffer ) { // already juxtapose ? 396 540 // no-op 397 541 } else { // must copy some text 398 if ( str1.Handle.s + s ize(str1) == VbyteAlloc(HeapArea, 0) ) { // str1 at end of string area ?399 VbyteAlloc( HeapArea, bsize); // create room for 2nd part at the end of string area542 if ( str1.Handle.s + str1.Handle.lnth == VbyteAlloc(*str1.Handle.ulink, 0) ) { // str1 at end of string area ? 543 VbyteAlloc( *str1.Handle.ulink, bsize ); // create room for 2nd part at the end of string area 400 544 } else { // copy the two parts 401 char * str1oldBuf = str1.Handle.s; 402 str1.Handle.s = VbyteAlloc( HeapArea, clnth ); 403 ByteCopy( HeapArea, str1.Handle.s, 0, str1.Handle.lnth, str1oldBuf, 0, str1.Handle.lnth); 545 char * str1newBuf = VbyteAlloc( *str1.Handle.ulink, clnth ); 546 char * str1oldBuf = str1.Handle.s; // must read after VbyteAlloc call in case it gs's 547 str1.Handle.s = str1newBuf; 548 memcpy( str1.Handle.s, str1oldBuf, str1.Handle.lnth ); 404 549 } // if 405 ByteCopy( HeapArea, str1.Handle.s, str1.Handle.lnth, bsize, (char*)buffer, 0, (int)bsize); 406 // VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth 550 memcpy( str1.Handle.s + str1.Handle.lnth, buffer, bsize ); 407 551 } // if 408 552 str1.Handle.lnth = clnth; … … 417 561 } 418 562 419 void ?+=?(string_res &s, const char* other) {420 append( s, other, strlen(other) );421 }422 563 423 564 … … 429 570 430 571 bool ?==?(const string_res &s1, const string_res &s2) { 431 return ByteCmp( HeapArea,s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0;572 return ByteCmp( s1.Handle.s, 0, s1.Handle.lnth, s2.Handle.s, 0, s2.Handle.lnth) == 0; 432 573 } 433 574 … … 455 596 456 597 int find(const string_res &s, char search) { 457 for (i; size(s)) { 458 if (s[i] == search) return i; 459 } 460 return size(s); 461 } 598 return findFrom(s, 0, search); 599 } 600 601 int findFrom(const string_res &s, size_t fromPos, char search) { 602 // FIXME: This paricular overload (find of single char) is optimized to use memchr. 603 // The general overload (find of string, memchr applying to its first character) and `contains` should be adjusted to match. 604 char * searchFrom = s.Handle.s + fromPos; 605 size_t searchLnth = s.Handle.lnth - fromPos; 606 int searchVal = search; 607 char * foundAt = (char *) memchr(searchFrom, searchVal, searchLnth); 608 if (foundAt == 0p) return s.Handle.lnth; 609 else return foundAt - s.Handle.s; 610 } 611 612 int find(const string_res &s, const string_res &search) { 613 return findFrom(s, 0, search); 614 } 615 616 int findFrom(const string_res &s, size_t fromPos, const string_res &search) { 617 return findFrom(s, fromPos, search.Handle.s, search.Handle.lnth); 618 } 619 620 int find(const string_res &s, const char* search) { 621 return findFrom(s, 0, search); 622 } 623 int findFrom(const string_res &s, size_t fromPos, const char* search) { 624 return findFrom(s, fromPos, search, strlen(search)); 625 } 626 627 int find(const string_res &s, const char* search, size_t searchsize) { 628 return findFrom(s, 0, search, searchsize); 629 } 630 631 int findFrom(const string_res &s, size_t fromPos, const char* search, size_t searchsize) { 462 632 463 633 /* Remaining implementations essentially ported from Sunjay's work */ 464 634 465 int find(const string_res &s, const string_res &search) { 466 return find(s, search.Handle.s, search.Handle.lnth); 467 } 468 469 int find(const string_res &s, const char* search) { 470 return find(s, search, strlen(search)); 471 } 472 473 int find(const string_res &s, const char* search, size_t searchsize) { 635 474 636 // FIXME: This is a naive algorithm. We probably want to switch to someting 475 637 // like Boyer-Moore in the future. … … 481 643 } 482 644 483 for (size_t i = 0; i < s.Handle.lnth; i++) {645 for (size_t i = fromPos; i < s.Handle.lnth; i++) { 484 646 size_t remaining = s.Handle.lnth - i; 485 647 // Never going to find the search string if the remaining string is … … 596 758 // Add a new HandleNode node n after the current HandleNode node. 597 759 598 static inlinevoid AddThisAfter( HandleNode & this, HandleNode & n ) with(this) {760 static void AddThisAfter( HandleNode & this, HandleNode & n ) with(this) { 599 761 #ifdef VbyteDebug 600 762 serr | "enter:AddThisAfter, this:" | &this | " n:" | &n; 601 763 #endif // VbyteDebug 764 // 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). 765 verify( n.ulink != 0p ); 766 verify( this.ulink == n.ulink ); 602 767 flink = n.flink; 603 768 blink = &n; … … 624 789 // Delete the current HandleNode node. 625 790 626 static inlinevoid DeleteNode( HandleNode & this ) with(this) {791 static void DeleteNode( HandleNode & this ) with(this) { 627 792 #ifdef VbyteDebug 628 793 serr | "enter:DeleteNode, this:" | &this; … … 638 803 639 804 // Allocates specified storage for a string from byte-string area. If not enough space remains to perform the 640 // allocation, the garbage collection routine is called and a second attempt is made to allocate the space. If the 641 // second attempt fails, a further attempt is made to create a new, larger byte-string area. 642 643 static inline char * VbyteAlloc( VbyteHeap & this, int size ) with(this) { 805 // allocation, the garbage collection routine is called. 806 807 static char * VbyteAlloc( VbyteHeap & this, int size ) with(this) { 644 808 #ifdef VbyteDebug 645 809 serr | "enter:VbyteAlloc, size:" | size; … … 650 814 NoBytes = ( uintptr_t )EndVbyte + size; 651 815 if ( NoBytes > ( uintptr_t )ExtVbyte ) { // enough room for new byte-string ? 652 garbage( this ); // firer up the garbage collector 653 NoBytes = ( uintptr_t )EndVbyte + size; // try again 654 if ( NoBytes > ( uintptr_t )ExtVbyte ) { // enough room for new byte-string ? 655 assert( 0 && "need to implement actual growth" ); 656 // extend( size ); // extend the byte-string area 657 } // if 816 garbage( this, size ); // firer up the garbage collector 817 verify( (( uintptr_t )EndVbyte + size) <= ( uintptr_t )ExtVbyte && "garbage run did not free up required space" ); 658 818 } // if 659 819 r = EndVbyte; … … 666 826 667 827 828 // Adjusts the last allocation in this heap by delta bytes, or resets this heap to be able to offer 829 // new allocations of its original size + delta bytes. Positive delta means bigger; 830 // negative means smaller. A null return indicates that the original heap location has room for 831 // the requested growth. A non-null return indicates that copying to a new location is required 832 // but has not been done; the returned value is the old heap storage location; `this` heap is 833 // modified to reference the new location. In the copy-requred case, the caller should use 834 // VbyteAlloc to claim the new space, while doing optimal copying from old to new, then free old. 835 836 static char * VbyteTryAdjustLast( VbyteHeap & this, int delta ) with(this) { 837 838 if ( ( uintptr_t )EndVbyte + delta <= ( uintptr_t )ExtVbyte ) { 839 // room available 840 EndVbyte += delta; 841 return 0p; 842 } 843 844 char *oldBytes = StartVbyte; 845 846 NoOfExtensions += 1; 847 CurrSize *= 2; 848 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize); 849 ExtVbyte = StartVbyte + CurrSize; 850 851 return oldBytes; 852 } 853 854 668 855 // Move an existing HandleNode node h somewhere after the current HandleNode node so that it is in ascending order by 669 856 // the address in the byte string area. 670 857 671 static inlinevoid MoveThisAfter( HandleNode & this, const HandleNode & h ) with(this) {858 static void MoveThisAfter( HandleNode & this, const HandleNode & h ) with(this) { 672 859 #ifdef VbyteDebug 673 860 serr | "enter:MoveThisAfter, this:" | & this | " h:" | & h; 674 861 #endif // VbyteDebug 862 verify( h.ulink != 0p ); 863 verify( this.ulink == h.ulink ); 675 864 if ( s < h.s ) { // check argument values 676 865 // serr | "VbyteSM: Error - Cannot move byte string starting at:" | s | " after byte string starting at:" 677 866 // | ( h->s ) | " and keep handles in ascending order"; 678 867 // exit(-1 ); 679 assert( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order");868 verify( 0 && "VbyteSM: Error - Cannot move byte strings as requested and keep handles in ascending order"); 680 869 } // if 681 870 … … 709 898 //######################### VbyteHeap ######################### 710 899 711 // Move characters from one location in the byte-string area to another. The routine handles the following situations:712 //713 // if the |Src| > |Dst| => truncate714 // if the |Dst| > |Src| => pad Dst with blanks715 716 void ByteCopy( VbyteHeap & this, char *Dst, int DstStart, int DstLnth, char *Src, int SrcStart, int SrcLnth ) {717 for ( int i = 0; i < DstLnth; i += 1 ) {718 if ( i == SrcLnth ) { // |Dst| > |Src|719 for ( ; i < DstLnth; i += 1 ) { // pad Dst with blanks720 Dst[DstStart + i] = ' ';721 } // for722 break;723 } // exit724 Dst[DstStart + i] = Src[SrcStart + i];725 } // for726 } // ByteCopy727 728 900 // Compare two byte strings in the byte-string area. The routine returns the following values: 729 901 // … … 732 904 // -1 => Src1-byte-string < Src2-byte-string 733 905 734 int ByteCmp( VbyteHeap & this, char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) with(this){906 int ByteCmp( char *Src1, int Src1Start, int Src1Lnth, char *Src2, int Src2Start, int Src2Lnth ) { 735 907 #ifdef VbyteDebug 736 908 serr | "enter:ByteCmp, Src1Start:" | Src1Start | " Src1Lnth:" | Src1Lnth | " Src2Start:" | Src2Start | " Src2Lnth:" | Src2Lnth; … … 789 961 h = Header.flink; // ignore header node 790 962 for (;;) { 791 ByteCopy( this, EndVbyte, 0, h->lnth, h->s, 0, h->lnth );963 memmove( EndVbyte, h->s, h->lnth ); 792 964 obase = h->s; 793 965 h->s = EndVbyte; … … 810 982 811 983 984 static double heap_expansion_freespace_threshold = 0.1; // default inherited from prior work: expand heap when less than 10% "free" (i.e. garbage) 985 // probably an unreasonable default, but need to assess early-round tests on changing it 986 987 void TUNING_set_string_heap_liveness_threshold( double val ) { 988 heap_expansion_freespace_threshold = 1.0 - val; 989 } 990 991 812 992 // Garbage determines the amount of free space left in the heap and then reduces, leave the same, or extends the size of 813 993 // the heap. The heap is then compacted in the existing heap or into the newly allocated heap. 814 994 815 void garbage(VbyteHeap & this ) with(this) {995 void garbage(VbyteHeap & this, int minreq ) with(this) { 816 996 #ifdef VbyteDebug 817 997 serr | "enter:garbage"; … … 837 1017 AmountFree = ( uintptr_t )ExtVbyte - ( uintptr_t )StartVbyte - AmountUsed; 838 1018 839 if ( AmountFree < ( int )( CurrSize * 0.1 )) { // free space less than 10% ? 840 841 assert( 0 && "need to implement actual growth" ); 842 // extend( CurrSize ); // extend the heap 1019 if ( ( double ) AmountFree < ( CurrSize * heap_expansion_freespace_threshold ) || AmountFree < minreq ) { // free space less than threshold or not enough to serve cur request 1020 1021 extend( this, max( CurrSize, minreq ) ); // extend the heap 843 1022 844 1023 // Peter says, "This needs work before it should be used." … … 846 1025 // reduce(( AmountFree / CurrSize - 3 ) * CurrSize ); // reduce the memory 847 1026 848 } // if 849 compaction(this); // compact the byte area, in the same or new heap area 1027 // `extend` implies a `compaction` during the copy 1028 1029 } else { 1030 compaction(this); // in-place 1031 }// if 850 1032 #ifdef VbyteDebug 851 1033 { … … 867 1049 #undef VbyteDebug 868 1050 869 //WIP870 #if 0871 1051 872 1052 … … 874 1054 // area is deleted. 875 1055 876 void VbyteHeap::extend( int size) {1056 void extend( VbyteHeap & this, int size ) with (this) { 877 1057 #ifdef VbyteDebug 878 1058 serr | "enter:extend, size:" | size; … … 884 1064 885 1065 CurrSize += size > InitSize ? size : InitSize; // minimum extension, initial size 886 StartVbyte = EndVbyte = new char[CurrSize];1066 StartVbyte = EndVbyte = TEMP_ALLOC(char, CurrSize); 887 1067 ExtVbyte = (void *)( StartVbyte + CurrSize ); 888 compaction( ); // copy from old heap to new & adjust pointers to new heap889 delete OldStartVbyte; // release old heap1068 compaction(this); // copy from old heap to new & adjust pointers to new heap 1069 free( OldStartVbyte ); // release old heap 890 1070 #ifdef VbyteDebug 891 1071 serr | "exit:extend, CurrSize:" | CurrSize; … … 893 1073 } // extend 894 1074 1075 //WIP 1076 #if 0 895 1077 896 1078 // Extend the size of the byte-string area by creating a new area and copying the old area into it. The old byte-string
Note:
See TracChangeset
for help on using the changeset viewer.