1 // Lowest level Text handling functions
2 // Copyright (C) 2000 Core Technologies.
3
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16
17 #include "includes.h"
18
19 // NOTE: text is managed using a "all in memory" model
20 // so, as far as most functions are concerned, the entire
21 // text data structure is kept in memory while it is being edited
22 // The OS swapping functions are charged with mapping the data in and out
23 //
24 // To manage the text data efficiently, and to keep memory
25 // from fragmenting, all text is kept in "Chunks"
26 // Each chunk allocates CHUNK_SIZE bytes of space from the free memory pool
27 // and the text of the document is split among them. The chunks are linked
28 // to each other in order. When text is inserted, additional chunks are allocated
29 // as necessary, and linked into the list at the appropriate location.
30 // Chunks keep track of how many bytes are currently used within them.
31 // Lines are allowed to span chunks.
32 // Chunks are not allowed to contain 0 bytes.
33
34
DisposeChunk(CHUNK_HEADER * chunk)35 static void DisposeChunk(CHUNK_HEADER *chunk)
36 // deallocate the memory used by chunk
37 {
38 MDisposePtr(chunk->data);
39 MDisposePtr(chunk);
40 }
41
CreateChunk()42 static CHUNK_HEADER *CreateChunk()
43 // create a chunk header, and its corresponding data
44 // if there is a problem, SetError, and return NULL
45 {
46 CHUNK_HEADER
47 *chunk;
48
49 if((chunk=(CHUNK_HEADER *)MNewPtrClr(sizeof(CHUNK_HEADER))))
50 {
51 if((chunk->data=(UINT8 *)MNewPtr(CHUNK_SIZE)))
52 {
53 return(chunk);
54 }
55 MDisposePtr(chunk);
56 }
57 return((CHUNK_HEADER *)NULL);
58 }
59
UniverseSanityCheck(TEXT_UNIVERSE * universe)60 bool UniverseSanityCheck(TEXT_UNIVERSE *universe)
61 // check the sanity of the text universe, report result
62 // if there is something wrong, crash or SetError, and return false
63 {
64 CHUNK_HEADER
65 *previousChunk,
66 *currentChunk;
67 UINT32
68 totalChunks,
69 actualTotalLines,
70 chunkTotalBytes,
71 chunkTotalLines;
72 UINT32
73 i;
74 bool
75 sawCachedChunk,
76 fail;
77
78 fail=sawCachedChunk=false;
79 chunkTotalBytes=0;
80 chunkTotalLines=0;
81 totalChunks=0;
82 previousChunk=NULL;
83 currentChunk=universe->firstChunkHeader;
84 while(currentChunk)
85 {
86 totalChunks++;
87 if(currentChunk==universe->cacheChunkHeader) // see if this is the cached chunk
88 {
89 sawCachedChunk=true;
90 if(chunkTotalLines!=universe->cacheLines)
91 {
92 SetError("Cache lines %d does not agree with %d\n",universe->cacheLines,chunkTotalLines);
93 fail=true;
94 }
95 if(chunkTotalBytes!=universe->cacheBytes)
96 {
97 SetError("Cache bytes %d does not agree with actual %d\n",universe->cacheBytes,chunkTotalBytes);
98 fail=true;
99 }
100 }
101 if(currentChunk->previousHeader==previousChunk)
102 {
103 if(currentChunk->totalBytes)
104 {
105 actualTotalLines=0;
106 for(i=0;i<currentChunk->totalBytes;i++)
107 {
108 if(currentChunk->data[i]=='\n')
109 {
110 actualTotalLines++;
111 }
112 }
113 if(actualTotalLines==currentChunk->totalLines)
114 {
115 chunkTotalBytes+=currentChunk->totalBytes;
116 chunkTotalLines+=currentChunk->totalLines;
117 previousChunk=currentChunk;
118 currentChunk=currentChunk->nextHeader;
119 }
120 else
121 {
122 SetError("Incorrect number of lines @ %X (he said there were %d, I saw %d)\n",currentChunk,currentChunk->totalLines,actualTotalLines);
123 fail=true;
124 }
125 }
126 else
127 {
128 SetError("Empty chunk @ %X\n",currentChunk);
129 fail=true;
130 }
131 }
132 else
133 {
134 SetError("Incorrect previous header %X @ %X\n",currentChunk->previousHeader,currentChunk);
135 fail=true;
136 }
137 }
138 if(!fail)
139 {
140 if(universe->lastChunkHeader==previousChunk)
141 {
142 if(chunkTotalBytes==universe->totalBytes)
143 {
144 if(chunkTotalLines==universe->totalLines)
145 {
146 if(!universe->cacheChunkHeader||sawCachedChunk)
147 {
148 if(totalChunks)
149 {
150 SetError("Total chunks %d, efficiency: %d%%\n",totalChunks,((universe->totalBytes/CHUNK_SIZE)*100)/(totalChunks));
151 }
152 else
153 {
154 SetError("Total chunks %d\n",totalChunks);
155 }
156 }
157 else
158 {
159 SetError("Invalid cache chunk header: %X\n",universe->cacheChunkHeader);
160 fail=true;
161 }
162 }
163 else
164 {
165 SetError("Total lines mismatch: chunks=%d, universe=%d\n",chunkTotalLines,universe->totalLines);
166 fail=true;
167 }
168 }
169 else
170 {
171 SetError("Total bytes mismatch: chunks=%d, universe=%d\n",chunkTotalBytes,universe->totalBytes);
172 fail=true;
173 }
174 }
175 else
176 {
177 SetError("The universe's last chunk did not match last one found\n");
178 fail=true;
179 }
180 }
181 return(!fail);
182 }
183
GetPosStartChunkAndDirection(TEXT_UNIVERSE * universe,UINT32 position,CHUNK_HEADER ** chunk,UINT32 * currentBytes,UINT32 * currentLines,bool * backwards)184 static void GetPosStartChunkAndDirection(TEXT_UNIVERSE *universe,UINT32 position,CHUNK_HEADER **chunk,UINT32 *currentBytes,UINT32 *currentLines,bool *backwards)
185 // return a chunk, and a direction to start searching,
186 // given a position that is desired
187 // NOTE: this uses the current cache information to help speed things up
188 // NOTE ALSO: position must not be out of range
189 {
190 if(universe->cacheChunkHeader) // if there is a cached chunk, see which direction we should go from it
191 {
192 *backwards=(position<universe->cacheBytes); // see which direction from the cache we need to go
193 *currentBytes=universe->cacheBytes;
194 *currentLines=universe->cacheLines;
195 *chunk=universe->cacheChunkHeader;
196 }
197 else
198 {
199 *backwards=false; // work from the start
200 *currentBytes=0;
201 *currentLines=0;
202 *chunk=universe->firstChunkHeader;
203 }
204 }
205
GetLineStartChunkAndDirection(TEXT_UNIVERSE * universe,UINT32 line,CHUNK_HEADER ** chunk,UINT32 * currentBytes,UINT32 * currentLines,bool * backwards)206 static void GetLineStartChunkAndDirection(TEXT_UNIVERSE *universe,UINT32 line,CHUNK_HEADER **chunk,UINT32 *currentBytes,UINT32 *currentLines,bool *backwards)
207 // return a chunk, and a direction to start searching,
208 // given a line number that is desired
209 // NOTE: this uses the current cache information to help speed things up
210 // NOTE ALSO: line must not be out of range
211 {
212 if(universe->cacheChunkHeader) // if there is a cached chunk, see which direction we should go from it
213 {
214 *backwards=(line<=universe->cacheLines); // see which direction from the cache we need to go
215 *currentBytes=universe->cacheBytes;
216 *currentLines=universe->cacheLines;
217 *chunk=universe->cacheChunkHeader;
218 }
219 else
220 {
221 *backwards=false; // work from the start
222 *currentBytes=0;
223 *currentLines=0;
224 *chunk=universe->firstChunkHeader;
225 }
226 }
227
PositionToChunkPositionPastEnd(TEXT_UNIVERSE * universe,UINT32 position,CHUNK_HEADER ** chunk,UINT32 * offset)228 void PositionToChunkPositionPastEnd(TEXT_UNIVERSE *universe,UINT32 position,CHUNK_HEADER **chunk,UINT32 *offset)
229 // locate the chunk which contains the byte at position within universe
230 // if there are no chunks in the universe, return chunk set to NULL, offset set to 0
231 // if the given position is past the end of the universe, return with chunk pointed to the
232 // last chunk of the universe, and offset as the last chunk's total-bytes
233 // NOTE: this will update the cache position to the chunk which is returned
234 {
235 CHUNK_HEADER
236 *currentChunk;
237 UINT32
238 currentPosition,
239 currentLine;
240 bool
241 backwards;
242
243 if(position>=universe->totalBytes) // quick test to get us to the end faster (and avoid some checks in the loop below)
244 {
245 if((universe->cacheChunkHeader=(*chunk)=universe->lastChunkHeader)) // point to the last chunk (if there is one)
246 {
247 universe->cacheBytes=universe->totalBytes-universe->lastChunkHeader->totalBytes;
248 universe->cacheLines=universe->totalLines-universe->lastChunkHeader->totalLines;
249 (*offset)=universe->lastChunkHeader->totalBytes; // pass back max offset
250 }
251 else
252 {
253 (*offset)=0;
254 }
255 }
256 else
257 {
258 GetPosStartChunkAndDirection(universe,position,¤tChunk,¤tPosition,¤tLine,&backwards);
259 if(backwards)
260 {
261 while(currentPosition>position)
262 {
263 currentChunk=currentChunk->previousHeader;
264 currentPosition-=currentChunk->totalBytes;
265 currentLine-=currentChunk->totalLines;
266 }
267 }
268 else
269 {
270 while((currentPosition+=currentChunk->totalBytes)<=position)
271 {
272 currentLine+=currentChunk->totalLines;
273 currentChunk=currentChunk->nextHeader;
274 }
275 currentPosition-=currentChunk->totalBytes;
276 }
277 universe->cacheChunkHeader=(*chunk)=currentChunk;
278 universe->cacheBytes=currentPosition;
279 universe->cacheLines=currentLine;
280 (*offset)=position-currentPosition;
281 }
282 }
283
PositionToChunkPosition(TEXT_UNIVERSE * universe,UINT32 position,CHUNK_HEADER ** chunk,UINT32 * offset)284 void PositionToChunkPosition(TEXT_UNIVERSE *universe,UINT32 position,CHUNK_HEADER **chunk,UINT32 *offset)
285 // locate the chunk which contains the byte at position within universe
286 // if there is no chunk in universe at position, return NULL/0
287 // NOTE: this will update the cache position to the chunk which is returned
288 {
289 PositionToChunkPositionPastEnd(universe,position,chunk,offset);
290 if((*chunk)&&((*offset)>=(*chunk)->totalBytes)) // if past end, then return NULL/0
291 {
292 *chunk=NULL;
293 *offset=0;
294 }
295 }
296
PositionToLinePosition(TEXT_UNIVERSE * universe,UINT32 position,UINT32 * line,UINT32 * lineOffset,CHUNK_HEADER ** chunk,UINT32 * chunkOffset)297 void PositionToLinePosition(TEXT_UNIVERSE *universe,UINT32 position,UINT32 *line,UINT32 *lineOffset,CHUNK_HEADER **chunk,UINT32 *chunkOffset)
298 // locate the line which contains the byte at position within universe
299 // also return chunk, and chunkOffset that point to the START of line
300 // if position is past the end of universe, line will be the last line
301 // and lineOffset will be one past the last character of line
302 // chunk/chunkOffset will point to the start of the last line (if there is one, NULL/0 if not)
303 // NOTE: this will update the cache position to the chunk which contained position
304 {
305 CHUNK_HEADER
306 *currentChunk;
307 UINT32
308 currentOffset,
309 currentPosition,
310 currentLine,
311 lineStartOffset,
312 numNewLines;
313 bool
314 backwards;
315
316 currentOffset=0;
317 if(position>=universe->totalBytes) // quick test to get us to the end faster (and avoid some checks in the loop below)
318 {
319 if((universe->cacheChunkHeader=currentChunk=universe->lastChunkHeader)) // point to the last chunk (if there is one)
320 {
321 universe->cacheBytes=universe->totalBytes-universe->lastChunkHeader->totalBytes;
322 universe->cacheLines=currentLine=universe->totalLines-universe->lastChunkHeader->totalLines;
323 currentOffset=universe->lastChunkHeader->totalBytes; // max offset to begin searching backwards from
324 }
325 }
326 else
327 {
328 GetPosStartChunkAndDirection(universe,position,¤tChunk,¤tPosition,¤tLine,&backwards);
329 if(backwards)
330 {
331 while(currentPosition>position)
332 {
333 currentChunk=currentChunk->previousHeader;
334 currentPosition-=currentChunk->totalBytes;
335 currentLine-=currentChunk->totalLines;
336 }
337 }
338 else
339 {
340 while((currentPosition+=currentChunk->totalBytes)<=position)
341 {
342 currentLine+=currentChunk->totalLines;
343 currentChunk=currentChunk->nextHeader;
344 }
345 currentPosition-=currentChunk->totalBytes;
346 }
347 universe->cacheChunkHeader=currentChunk;
348 universe->cacheBytes=currentPosition;
349 universe->cacheLines=currentLine;
350 currentOffset=position-currentPosition; // offset to the given position
351 }
352
353 *lineOffset=0; // at the moment, no offset
354 if(currentChunk) // if this is valid, then there were some bytes in the universe
355 {
356 // walk backwards from the current position until the beginning of the current chunk, and then until the first newline encountered (if there are any)
357 numNewLines=0; // tells how many new lines seen in this chunk backwards from current position
358 lineStartOffset=0;
359 while(currentOffset)
360 {
361 if(currentChunk->data[--currentOffset]=='\n')
362 {
363 if(!numNewLines)
364 {
365 lineStartOffset=currentOffset+1; // offset into the chunk that points to the start of the line
366 }
367 numNewLines++;
368 }
369 if(!numNewLines) // increment length into the line until a newline is seen
370 {
371 (*lineOffset)++;
372 }
373 }
374 *line=currentLine+numNewLines; // this tells which line the position fell into
375 if(!numNewLines)
376 {
377 while(currentChunk->previousHeader&&!(currentChunk->previousHeader->totalLines)) // push back through all chunks which do not contain new lines
378 {
379 currentChunk=currentChunk->previousHeader;
380 (*lineOffset)+=currentChunk->totalBytes; // this many more bytes into the line
381 }
382 if(currentChunk->previousHeader) // step back into one which contains newlines
383 {
384 currentChunk=currentChunk->previousHeader;
385 currentOffset=lineStartOffset=currentChunk->totalBytes;
386 }
387 while(currentOffset&¤tChunk->data[--currentOffset]!='\n')
388 {
389 (*lineOffset)++; // keep moving back
390 lineStartOffset=currentOffset; // offset in chunk to start of line
391 }
392 }
393 if(lineStartOffset>=currentChunk->totalBytes)
394 {
395 *chunk=currentChunk->nextHeader; // move one past (possibly off the end)
396 *chunkOffset=0;
397 }
398 else
399 {
400 *chunk=currentChunk;
401 *chunkOffset=lineStartOffset;
402 }
403 }
404 else
405 {
406 *chunk=NULL;
407 *chunkOffset=0;
408 *line=universe->totalLines; // since it is past the end, the line is this, work backwards to get the offset
409 }
410 }
411
LineToChunkPosition(TEXT_UNIVERSE * universe,UINT32 line,CHUNK_HEADER ** chunk,UINT32 * offset,UINT32 * position)412 void LineToChunkPosition(TEXT_UNIVERSE *universe,UINT32 line,CHUNK_HEADER **chunk,UINT32 *offset,UINT32 *position)
413 // locate the chunk which contains the byte at the start of line within universe
414 // position is the absolute offset within the text to the start of line
415 // if no chunk contains a byte that starts line, chunk will be NULL, offset will be 0
416 // position will be the number of bytes in the text buffer
417 // NOTE: this will update the cache position to the chunk which is returned
418 {
419 CHUNK_HEADER
420 *currentChunk;
421 UINT32
422 currentByte,
423 currentLine;
424 bool
425 backwards;
426
427 if(line>universe->totalLines||!(universe->firstChunkHeader))
428 {
429 if((universe->cacheChunkHeader=universe->lastChunkHeader)) // point to the last chunk (if there is one)
430 {
431 universe->cacheBytes=universe->totalBytes-universe->lastChunkHeader->totalBytes;
432 universe->cacheLines=currentLine=universe->totalLines-universe->lastChunkHeader->totalLines;
433 }
434 (*chunk)=NULL;
435 (*offset)=0;
436 (*position)=universe->totalBytes;
437 }
438 else
439 {
440 GetLineStartChunkAndDirection(universe,line,¤tChunk,¤tByte,¤tLine,&backwards); // find out where to start searching from
441 if(backwards)
442 {
443 while(currentChunk->previousHeader&¤tLine>=line) // run through the chunks until we get to the one BEFORE or during which this line starts
444 {
445 currentChunk=currentChunk->previousHeader;
446 currentLine-=currentChunk->totalLines;
447 currentByte-=currentChunk->totalBytes;
448 }
449 }
450 else
451 {
452 while((currentLine+=currentChunk->totalLines)<line) // run through the chunks looking for the start of the given line
453 {
454 currentByte+=currentChunk->totalBytes;
455 currentChunk=currentChunk->nextHeader; // keep going
456 }
457 currentLine-=currentChunk->totalLines; // back up, to start of chunk which contains the requested line
458 }
459 *offset=0;
460 while(currentLine<line) // move through until enough lines have been counted
461 {
462 if(currentChunk->data[(*offset)++]=='\n')
463 {
464 currentLine++;
465 }
466 currentByte++;
467 }
468 (*position)=currentByte;
469 (*chunk)=currentChunk;
470 if((*offset)>=currentChunk->totalBytes) // if needed, move to the next chunk
471 {
472 (*chunk)=currentChunk->nextHeader;
473 (*offset)=0;
474 }
475 }
476 }
477
478
AddToChunkPosition(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** newChunk,UINT32 * newOffset,UINT32 distanceToMove)479 void AddToChunkPosition(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **newChunk,UINT32 *newOffset,UINT32 distanceToMove)
480 // move forward from chunk/offset by distanceToMove
481 // if distanceToMove pushes us off the end, return NULL, 0
482 // It is ok to pass newChunk as a pointer to chunk
483 // NOTE: this does not update the cache position
484 {
485 while(chunk&&(distanceToMove>=(chunk->totalBytes-offset)))
486 {
487 distanceToMove-=(chunk->totalBytes-offset);
488 chunk=chunk->nextHeader;
489 offset=0;
490 }
491 if((*newChunk=chunk))
492 {
493 *newOffset=offset+distanceToMove;
494 }
495 else
496 {
497 *newOffset=0;
498 }
499 }
500
ChunkPositionToNextLine(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** newChunk,UINT32 * newOffset,UINT32 * distanceMoved)501 void ChunkPositionToNextLine(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **newChunk,UINT32 *newOffset,UINT32 *distanceMoved)
502 // locate the line that begins after chunk, and offset
503 // if chunk is NULL, return NULL, and distanceMoved=0, otherwise
504 // attempt to locate the start of the next line, returning chunk, and offset.
505 // if there is no next line start, return NULL, and distance moved as the amount we traversed before
506 // discovering there was no next line
507 // It is ok to pass newChunk as a pointer to chunk
508 // NOTE: this does not update the cache position
509 {
510 bool
511 found;
512
513 (*distanceMoved)=0;
514
515 if(chunk)
516 {
517 found=false;
518 if(chunk->totalLines) // see if there are any lines in this chunk (if not, dont bother looking for next new line)
519 {
520 while(!found&&offset<chunk->totalBytes)
521 {
522 if(chunk->data[offset]=='\n')
523 {
524 found=true;
525 }
526 else
527 {
528 (*distanceMoved)++;
529 offset++;
530 }
531 }
532 if(!found)
533 {
534 chunk=chunk->nextHeader;
535 offset=0;
536 }
537 }
538 else
539 {
540 (*distanceMoved)=chunk->totalBytes-offset; // if no lines, skip the rest of the first chunk
541 chunk=chunk->nextHeader;
542 offset=0;
543 }
544
545 if(!found)
546 {
547 while(chunk&&!chunk->totalLines) // run through the chunks looking for one that contains a line
548 {
549 (*distanceMoved)+=chunk->totalBytes;
550 chunk=chunk->nextHeader; // skip all chunks with no lines in them
551 }
552 if(chunk) // this is the chunk that contains the new line we were looking for, so locate it
553 {
554 while(chunk->data[offset]!='\n')
555 {
556 offset++;
557 }
558 (*distanceMoved)+=offset;
559 found=true;
560 }
561 }
562 if(found) // at this point, we have found the new line, chunk, and offset point to the new line character, so skip over it, and return what's next
563 {
564 (*distanceMoved)++;
565 offset++;
566 if(offset>=chunk->totalBytes) // push through to the next chunk
567 {
568 chunk=chunk->nextHeader;
569 offset=0;
570 }
571 }
572 }
573 (*newChunk)=chunk;
574 (*newOffset)=offset;
575 }
576
CopyTextToChunk(CHUNK_HEADER * chunk,UINT32 offset,UINT8 * text,UINT32 numBytes,UINT32 * numLines)577 static void CopyTextToChunk(CHUNK_HEADER *chunk,UINT32 offset,UINT8 *text,UINT32 numBytes,UINT32 *numLines)
578 // copy text into chunk at offset for numBytes
579 // also count up how many new-lines were seen during the copy
580 // return numLines as the total number of new-line characters that were copied
581 {
582 UINT8
583 *dst;
584 UINT32
585 i;
586
587 dst=&(chunk->data[offset]);
588 (*numLines)=0;
589 for(i=0;i<numBytes;i++)
590 {
591 if((dst[i]=text[i])=='\n')
592 {
593 (*numLines)++;
594 }
595 }
596 }
597
CopyChunkToChunk(CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** sourceChunk,UINT32 * sourceOffset,UINT32 numBytes,UINT32 * numLines)598 static void CopyChunkToChunk(CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **sourceChunk,UINT32 *sourceOffset,UINT32 numBytes,UINT32 *numLines)
599 // copy the text starting at sourceChunk/sourceOffset into chunk at offset for numBytes
600 // also count up how many new-lines were seen during the copy
601 // return numLines as the total number of new-line characters that were copied
602 // also return with sourceChunk/sourceOffset pointing to the byte just after the last one copied
603 {
604 CHUNK_HEADER
605 *sChunk;
606 UINT8
607 *src,
608 *dst;
609 UINT32
610 i,
611 sOffset,
612 sourceLimit;
613
614 dst=&(chunk->data[offset]);
615 (*numLines)=0;
616 if((sChunk=(*sourceChunk)))
617 {
618 src=sChunk->data;
619 sOffset=*sourceOffset;
620 sourceLimit=sChunk->totalBytes;
621 for(i=0;i<numBytes&&sChunk;i++)
622 {
623 if((dst[i]=src[sOffset])=='\n')
624 {
625 (*numLines)++;
626 }
627 if(++sOffset>=sourceLimit)
628 {
629 sOffset=0;
630 if((sChunk=sChunk->nextHeader))
631 {
632 src=sChunk->data;
633 sourceLimit=sChunk->totalBytes;
634 }
635 }
636 }
637 (*sourceChunk)=sChunk;
638 (*sourceOffset)=sOffset;
639 }
640 }
641
DetermineNewLines(CHUNK_HEADER * chunk,UINT32 offset,UINT32 numBytes,UINT32 * numLines)642 static void DetermineNewLines(CHUNK_HEADER *chunk,UINT32 offset,UINT32 numBytes,UINT32 *numLines)
643 // count the number of new-line characters at offset of chunk, for numBytes
644 // return numLines as the total number of new-line characters that were counted
645 {
646 UINT8
647 *dst;
648 UINT32
649 i;
650
651 dst=&(chunk->data[offset]);
652 (*numLines)=0;
653 for(i=0;i<numBytes;i++)
654 {
655 if((dst[i])=='\n')
656 {
657 (*numLines)++;
658 }
659 }
660 }
661
DisposeChunkList(CHUNK_HEADER * startChunk)662 static void DisposeChunkList(CHUNK_HEADER *startChunk)
663 // dispose of a linked list of chunks
664 {
665 CHUNK_HEADER
666 *nextChunk;
667
668 while(startChunk)
669 {
670 nextChunk=startChunk->nextHeader;
671 DisposeChunk(startChunk);
672 startChunk=nextChunk;
673 }
674 }
675
CreateChunkList(CHUNK_HEADER ** startChunk,CHUNK_HEADER ** endChunk,UINT32 numChunks)676 static bool CreateChunkList(CHUNK_HEADER **startChunk,CHUNK_HEADER **endChunk,UINT32 numChunks)
677 // create a linked list of empty chunks
678 // if there is a problem, SetError, free anything allocated, and return false
679 {
680 bool
681 fail;
682 CHUNK_HEADER
683 *chunk;
684
685 (*startChunk)=(*endChunk)=NULL;
686 fail=false;
687 while(numChunks&&!fail)
688 {
689 if((chunk=CreateChunk()))
690 {
691 if(*endChunk)
692 {
693 (*endChunk)->nextHeader=chunk;
694 chunk->previousHeader=(*endChunk);
695 (*endChunk)=chunk;
696 }
697 else
698 {
699 (*startChunk)=(*endChunk)=chunk;
700 }
701 numChunks--;
702 }
703 else
704 {
705 DisposeChunkList(*startChunk); // remove the partially created chunk list
706 fail=true;
707 }
708 }
709 return(!fail);
710 }
711
712 // Text insertion cases
713 // these routines handle all of the possible cases that can occur while
714 // inserting text
715
InsertWithRoomInChunk(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** textChunk,UINT32 * textOffset,UINT32 numBytes)716 static bool InsertWithRoomInChunk(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **textChunk,UINT32 *textOffset,UINT32 numBytes)
717 // the chunk that the text is going to be inserted into
718 // contains enough free space to accept the entire inserted text
719 {
720 UINT32
721 linesInserted;
722
723 MMoveMem(&(chunk->data[offset]),&(chunk->data[offset+numBytes]),chunk->totalBytes-offset); // move data within the chunk to make room
724 CopyChunkToChunk(chunk,offset,textChunk,textOffset,numBytes,&linesInserted);
725 chunk->totalBytes+=numBytes; // bump number of bytes in this chunk
726 chunk->totalLines+=linesInserted; // bump number of lines in this chunk
727 universe->totalBytes+=numBytes; // adjust universal understanding
728 universe->totalLines+=linesInserted;
729 return(true); // this cannot fail
730 }
731
InsertWithRoomInTwoChunks(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** textChunk,UINT32 * textOffset,UINT32 numBytes)732 static bool InsertWithRoomInTwoChunks(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **textChunk,UINT32 *textOffset,UINT32 numBytes)
733 // the chunk where the text insertion starts, and the next chunk contain enough free space
734 // to allow the text to be inserted
735 {
736 CHUNK_HEADER
737 *secondChunk;
738 UINT32
739 linesInserted,
740 tempInserted;
741 UINT32
742 amountToMove,
743 distanceToMove;
744
745 secondChunk=chunk->nextHeader;
746 if(numBytes+offset<=CHUNK_SIZE) // see if the added text can be contained completely within the first chunk
747 {
748 distanceToMove=numBytes-(CHUNK_SIZE-chunk->totalBytes); // for speed, we would like to move as much as possible to the second chunk, but for memory efficiency, we would like to move as little as possible! (memory efficiency turns out to be better)
749
750 MMoveMem(secondChunk->data,&(secondChunk->data[distanceToMove]),secondChunk->totalBytes); // move second chunk's data out of the way for the add
751 CopyTextToChunk(secondChunk,0,&(chunk->data[chunk->totalBytes-distanceToMove]),distanceToMove,&tempInserted); // move data from first chunk to second chunk
752 chunk->totalBytes-=distanceToMove;
753 secondChunk->totalBytes+=distanceToMove; // this many more bytes now in use in second chunk
754 chunk->totalLines-=tempInserted;
755 secondChunk->totalLines+=tempInserted;
756
757 MMoveMem(&(chunk->data[offset]),&(chunk->data[offset+numBytes]),chunk->totalBytes-offset); // move data within the chunk to make room
758 CopyChunkToChunk(chunk,offset,textChunk,textOffset,numBytes,&linesInserted); // copy the data into the chunk
759 chunk->totalBytes+=numBytes; // bump number of bytes in this chunk
760 chunk->totalLines+=linesInserted; // bump number of lines in this chunk
761 universe->totalBytes+=numBytes; // adjust universal understanding
762 universe->totalLines+=linesInserted;
763 }
764 else
765 {
766 distanceToMove=numBytes-(CHUNK_SIZE-chunk->totalBytes); // need to push second chunk data forward this distance
767 MMoveMem(secondChunk->data,&(secondChunk->data[distanceToMove]),secondChunk->totalBytes); // move second chunk's data out of the way for the add
768 secondChunk->totalBytes+=distanceToMove; // this many more bytes now in use in second chunk
769 amountToMove=chunk->totalBytes-offset; // number of bytes to move from first to second
770
771 CopyTextToChunk(secondChunk,offset+numBytes-CHUNK_SIZE,&(chunk->data[offset]),amountToMove,&tempInserted); // move data from first chunk to second chunk
772 chunk->totalLines-=tempInserted;
773 secondChunk->totalLines+=tempInserted;
774
775 CopyChunkToChunk(chunk,offset,textChunk,textOffset,CHUNK_SIZE-offset,&tempInserted); // copy source text into first chunk
776 chunk->totalBytes=CHUNK_SIZE;
777 chunk->totalLines+=tempInserted;
778
779 CopyChunkToChunk(secondChunk,0,textChunk,textOffset,numBytes-(CHUNK_SIZE-offset),&linesInserted); // copy remaining text into second chunk
780 secondChunk->totalLines+=linesInserted;
781 universe->totalLines+=linesInserted+tempInserted; // add number of lines added in first chunk
782
783 universe->totalBytes+=numBytes; // adjust universal understanding
784 }
785 return(true); // this cannot fail
786 }
787
InsertWithNoRoom(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** textChunk,UINT32 * textOffset,UINT32 numBytes)788 static bool InsertWithNoRoom(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **textChunk,UINT32 *textOffset,UINT32 numBytes)
789 // the text to be inserted cannot fit within its two adjacent chunks
790 // so, chunks will have to be created, and the text put into them
791 {
792 UINT32
793 linesInserted,
794 tempInserted;
795 UINT32
796 numChunks,
797 maxToMove,
798 bytesToMove;
799 CHUNK_HEADER
800 *startChunk,
801 *endChunk;
802
803 numChunks=(numBytes-(CHUNK_SIZE-(chunk->totalBytes))+CHUNK_SIZE-1)/CHUNK_SIZE;
804 if(CreateChunkList(&startChunk,&endChunk,numChunks)) // create just enough chunks to hold the data
805 {
806 chunk->nextHeader->previousHeader=endChunk; // link new chunks onto the list
807 endChunk->nextHeader=chunk->nextHeader;
808 chunk->nextHeader=startChunk;
809 startChunk->previousHeader=chunk;
810 universe->totalBytes+=numBytes; // more bytes in the universe now
811
812 if(numBytes<(CHUNK_SIZE-offset)) // see if the bytes we are adding can be added into the first chunk
813 {
814 bytesToMove=chunk->totalBytes+numBytes-CHUNK_SIZE;
815 CopyTextToChunk(endChunk,0,&(chunk->data[chunk->totalBytes-bytesToMove]),bytesToMove,&tempInserted); // move data from old last chunk to new last chunk
816 endChunk->totalBytes=bytesToMove; // this many bytes in end chunk
817 endChunk->totalLines=tempInserted;
818 chunk->totalLines-=tempInserted;
819 MMoveMem(&(chunk->data[offset]),&(chunk->data[offset+numBytes]),chunk->totalBytes-bytesToMove-offset); // move data within the original chunk to make room
820
821 CopyChunkToChunk(chunk,offset,textChunk,textOffset,numBytes,&tempInserted); // copy text into this chunk
822 chunk->totalBytes=CHUNK_SIZE; // this is now completely full
823 chunk->totalLines+=tempInserted; // added this many lines
824 universe->totalLines+=tempInserted; // add to the universe too
825 }
826 else
827 {
828 if((numBytes+offset)/CHUNK_SIZE<numChunks) // see if we can move the stuff after the given offset all the way into the end chunk
829 {
830 CopyTextToChunk(endChunk,0,&(chunk->data[offset]),chunk->totalBytes-offset,&tempInserted); // move data from old last chunk to new last chunk
831 }
832 else
833 {
834 if(chunk->totalBytes==offset) // minor kludge to make sure we are left pointing to the correct spot
835 {
836 tempInserted=0;
837 }
838 else
839 {
840 CopyTextToChunk(endChunk,(numBytes+offset)%CHUNK_SIZE,&(chunk->data[offset]),chunk->totalBytes-offset,&tempInserted); // move data from old last chunk to new last chunk
841 }
842 }
843 endChunk->totalBytes=chunk->totalBytes-offset; // this many bytes in end chunk at the moment
844 chunk->totalBytes=offset; // this many bytes in here
845 chunk->totalLines-=tempInserted; // removed lines from here
846 endChunk->totalLines=tempInserted; // placed them here
847 linesInserted=0;
848 while(numBytes&&chunk)
849 {
850 maxToMove=CHUNK_SIZE-offset; // can move this many bytes
851 if(numBytes>maxToMove)
852 {
853 bytesToMove=maxToMove;
854 }
855 else
856 {
857 bytesToMove=numBytes;
858 }
859 CopyChunkToChunk(chunk,offset,textChunk,textOffset,bytesToMove,&tempInserted); // copy text into this chunk
860 chunk->totalBytes+=bytesToMove; // bump number of bytes in this chunk
861 chunk->totalLines+=tempInserted; // bump number of lines in this chunk
862 linesInserted+=tempInserted; // keep track of total number of lines inserted so far
863 numBytes-=bytesToMove; // this many less bytes to move now
864 chunk=chunk->nextHeader; // point to next chunk to fill
865 offset=0; // from this point on, the offset is always 0
866 }
867 universe->totalLines+=linesInserted; // increment the number of lines in the universe
868 }
869 return(true); // done
870 }
871 return(false);
872 }
873
InsertAtLastChunk(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,CHUNK_HEADER ** textChunk,UINT32 * textOffset,UINT32 numBytes)874 static bool InsertAtLastChunk(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,CHUNK_HEADER **textChunk,UINT32 *textOffset,UINT32 numBytes)
875 // text is being inserted into the last chunk of the universe, and
876 // the chunk does not contain enough free space to hold it
877 // NOTE: this code must handle the case where offset is CHUNK_SIZE
878 {
879 UINT32
880 linesInserted,
881 tempInserted;
882 UINT32
883 numChunks,
884 maxToMove,
885 bytesToMove;
886 CHUNK_HEADER
887 *startChunk,
888 *endChunk;
889
890 numChunks=(numBytes-(CHUNK_SIZE-(chunk->totalBytes))+CHUNK_SIZE-1)/CHUNK_SIZE;
891 if(CreateChunkList(&startChunk,&endChunk,numChunks)) // create just enough chunks to hold the data
892 {
893 chunk->nextHeader=startChunk; // link new chunks onto the list
894 startChunk->previousHeader=chunk;
895 universe->lastChunkHeader=endChunk; // update the universe
896 universe->totalBytes+=numBytes; // more bytes in the universe now
897 if((numBytes+offset)/CHUNK_SIZE<numChunks)
898 {
899 CopyTextToChunk(endChunk,0,&(chunk->data[offset]),chunk->totalBytes-offset,&tempInserted); // move data from old last chunk to new last chunk
900 }
901 else
902 {
903 if(chunk->totalBytes==offset) // minor kludge to make sure we are left pointing to the correct spot
904 {
905 tempInserted=0;
906 }
907 else
908 {
909 CopyTextToChunk(endChunk,(numBytes+offset)%CHUNK_SIZE,&(chunk->data[offset]),chunk->totalBytes-offset,&tempInserted); // move data from old last chunk to new last chunk
910 }
911 }
912 endChunk->totalBytes=chunk->totalBytes-offset; // this many bytes in end chunk at the moment
913 chunk->totalBytes=offset; // this many bytes in here
914 chunk->totalLines-=tempInserted; // removed lines from here
915 endChunk->totalLines=tempInserted; // placed them here
916 linesInserted=0;
917 while(numBytes&&chunk)
918 {
919 maxToMove=CHUNK_SIZE-offset; // can move this many bytes
920 if(numBytes>maxToMove)
921 {
922 bytesToMove=maxToMove;
923 }
924 else
925 {
926 bytesToMove=numBytes;
927 }
928 CopyChunkToChunk(chunk,offset,textChunk,textOffset,bytesToMove,&tempInserted); // copy text into this chunk
929 chunk->totalBytes+=bytesToMove; // bump number of bytes in this chunk
930 chunk->totalLines+=tempInserted; // bump number of lines in this chunk
931 linesInserted+=tempInserted; // keep track of total number of lines inserted so far
932 numBytes-=bytesToMove; // this many less bytes to move now
933 chunk=chunk->nextHeader; // point to next chunk to fill
934 offset=0; // from this point on, the offset is always 0
935 }
936 universe->totalLines+=linesInserted; // increment the number of lines in the universe
937 return(true); // done
938 }
939 return(false);
940 }
941
InsertWithNoChunks(TEXT_UNIVERSE * universe,CHUNK_HEADER ** textChunk,UINT32 * textOffset,UINT32 numBytes)942 static bool InsertWithNoChunks(TEXT_UNIVERSE *universe,CHUNK_HEADER **textChunk,UINT32 *textOffset,UINT32 numBytes)
943 // text insertion is happening in an empty text universe
944 {
945 UINT32
946 linesInserted,
947 tempInserted;
948 UINT32
949 bytesToMove;
950 CHUNK_HEADER
951 *chunk,
952 *startChunk,
953 *endChunk;
954
955 if(CreateChunkList(&startChunk,&endChunk,(numBytes+CHUNK_SIZE-1)/CHUNK_SIZE)) // create just enough chunks to hold the data
956 {
957 universe->firstChunkHeader=startChunk;
958 universe->lastChunkHeader=endChunk;
959 universe->totalBytes=numBytes;
960 chunk=startChunk;
961 linesInserted=0;
962 while(chunk&&numBytes)
963 {
964 if(numBytes>CHUNK_SIZE)
965 {
966 bytesToMove=CHUNK_SIZE;
967 }
968 else
969 {
970 bytesToMove=numBytes;
971 }
972 CopyChunkToChunk(chunk,0,textChunk,textOffset,bytesToMove,&tempInserted); // copy the data into the chunk
973 chunk->totalBytes=bytesToMove; // set number of bytes in this chunk
974 chunk->totalLines=tempInserted; // set number of lines in this chunk
975 linesInserted+=tempInserted; // keep track of total number of lines inserted so far
976 numBytes-=bytesToMove; // this many less bytes to move now
977 chunk=chunk->nextHeader; // point to next chunk to fill
978 }
979 universe->totalLines=linesInserted; // remember the number of lines in the universe
980 return(true); // done
981 }
982 return(false);
983 }
984
InsertUniverseChunks(TEXT_UNIVERSE * universe,UINT32 position,CHUNK_HEADER * textChunk,UINT32 textOffset,UINT32 numBytes)985 bool InsertUniverseChunks(TEXT_UNIVERSE *universe,UINT32 position,CHUNK_HEADER *textChunk,UINT32 textOffset,UINT32 numBytes)
986 // insert numBytes of text from textChunk/textOffset into universe starting at position
987 // if there is a problem, SetError, and return false
988 // this routine is allowed to either succeed, or fail completely, under no circumstance
989 // is it allowed to insert only part of the given text
990 // NOTE: it is illegal for textChunk/textOffset to point to less data than numBytes
991 // NOTE ALSO: to accommodate inserting unchunked text, textChunk is allowed to be
992 // larger than CHUNK_SIZE, and textChunk's totalLines field does not have to be accurate
993 // Finally, this is does not guarantee that it will not alter the chunk list of
994 // universe before the point of insertion.
995 // NOTE: if bytes are inserted, this will update the cache to point to the chunk that the insertion started in
996 {
997 bool
998 fail;
999 CHUNK_HEADER
1000 *chunk;
1001 UINT32
1002 offset;
1003
1004 fail=false;
1005 if(numBytes) // only do more work if bytes to insert
1006 {
1007 PositionToChunkPosition(universe,position,&chunk,&offset); // locate the position, move the cache there
1008 if(chunk) // see if inserting within the text
1009 {
1010 if(offset>chunk->totalBytes) // if this is out of range, fudge it back!
1011 {
1012 offset=chunk->totalBytes;
1013 }
1014 if((offset==0)&&(chunk->previousHeader)&&(chunk->previousHeader->totalBytes<CHUNK_SIZE)) // if inserting at the very start of a chunk, it is more efficient to look one back (if possible)
1015 {
1016 universe->cacheChunkHeader=chunk=chunk->previousHeader;
1017 universe->cacheBytes-=chunk->totalBytes; // move the cache back one, so it will not be effected by the insert
1018 universe->cacheLines-=chunk->totalLines;
1019 offset=chunk->totalBytes;
1020 }
1021 if(numBytes<=CHUNK_SIZE-(chunk->totalBytes)) // see if the bytes can be placed in empty space of this chunk
1022 {
1023 fail=!InsertWithRoomInChunk(universe,chunk,offset,&textChunk,&textOffset,numBytes);
1024 }
1025 else
1026 {
1027 if(chunk->nextHeader) // is there another chunk after this one?
1028 {
1029 if(numBytes<=(2*CHUNK_SIZE)-(chunk->totalBytes+chunk->nextHeader->totalBytes)) // is there room in the combined free space of this chunk, and the next one?
1030 {
1031 fail=!InsertWithRoomInTwoChunks(universe,chunk,offset,&textChunk,&textOffset,numBytes);
1032 }
1033 else
1034 {
1035 fail=!InsertWithNoRoom(universe,chunk,offset,&textChunk,&textOffset,numBytes);
1036 }
1037 }
1038 else // last chunk does not have enough space to hold the text
1039 {
1040 fail=!InsertAtLastChunk(universe,chunk,offset,&textChunk,&textOffset,numBytes);
1041 }
1042 }
1043 }
1044 else
1045 {
1046 // inserting text at the very end (cache is set to NULL in this case)
1047 if((chunk=universe->lastChunkHeader))
1048 {
1049 if(numBytes<=CHUNK_SIZE-(chunk->totalBytes)) // see if the bytes can be placed in empty space of this chunk
1050 {
1051 fail=!InsertWithRoomInChunk(universe,chunk,chunk->totalBytes,&textChunk,&textOffset,numBytes);
1052 }
1053 else
1054 {
1055 fail=!InsertAtLastChunk(universe,chunk,chunk->totalBytes,&textChunk,&textOffset,numBytes);
1056 }
1057 }
1058 else
1059 {
1060 fail=!InsertWithNoChunks(universe,&textChunk,&textOffset,numBytes);
1061 }
1062 }
1063 }
1064 return(!fail);
1065 }
1066
InsertUniverseText(TEXT_UNIVERSE * universe,UINT32 position,UINT8 * text,UINT32 numBytes)1067 bool InsertUniverseText(TEXT_UNIVERSE *universe,UINT32 position,UINT8 *text,UINT32 numBytes)
1068 // insert numBytes of text into universe starting at position
1069 // if there is a problem, SetError, and return false
1070 // this routine is allowed to either succeed, or fail completely, under no circumstance
1071 // is it allowed to insert only part of the given text
1072 // if there is a problem, SetError, and return false
1073 // NOTE: this will update the cache to point to the chunk that the insertion started in
1074 {
1075 CHUNK_HEADER
1076 psuedoChunk; // used to create a chunk that points to text
1077
1078 psuedoChunk.previousHeader=psuedoChunk.nextHeader=NULL; // set up fake chunk header, so we can call insert chunk routine
1079 psuedoChunk.data=text;
1080 psuedoChunk.totalBytes=numBytes;
1081 psuedoChunk.totalLines=0; // this does not have to be set accurately
1082 return(InsertUniverseChunks(universe,position,&psuedoChunk,0,numBytes));
1083 }
1084
ExtractUniverseText(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,UINT8 * text,UINT32 numBytes,CHUNK_HEADER ** nextChunk,UINT32 * nextOffset)1085 bool ExtractUniverseText(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,UINT8 *text,UINT32 numBytes,CHUNK_HEADER **nextChunk,UINT32 *nextOffset)
1086 // extract numBytes of text from universe at chunk/offset
1087 // if there are not numBytes of text in universe at chunk/offset, then
1088 // as many as possible will be returned
1089 // it is acceptable to pass chunk as NULL, in which case, no bytes will ever be returned
1090 // text must be large enough to accept numBytes or bad things will happen
1091 // if there is a problem, SetError, and return false
1092 {
1093 UINT32
1094 numToExtract;
1095
1096 while(numBytes&&chunk)
1097 {
1098 if((chunk->totalBytes-offset)<numBytes)
1099 {
1100 numToExtract=chunk->totalBytes-offset;
1101 }
1102 else
1103 {
1104 numToExtract=numBytes;
1105 }
1106 MMoveMem(&chunk->data[offset],text,numToExtract);
1107 numBytes-=numToExtract;
1108 text+=numToExtract;
1109 offset+=numToExtract;
1110 if(offset>=chunk->totalBytes)
1111 {
1112 chunk=chunk->nextHeader;
1113 offset=0;
1114 }
1115 }
1116 (*nextChunk)=chunk;
1117 (*nextOffset)=offset;
1118 return(true);
1119 }
1120
UnlinkChunkList(TEXT_UNIVERSE * universe,CHUNK_HEADER * startChunk,CHUNK_HEADER * endChunk)1121 static void UnlinkChunkList(TEXT_UNIVERSE *universe,CHUNK_HEADER *startChunk,CHUNK_HEADER *endChunk)
1122 // unlink the chunks from startChunk to endChunk from universe
1123 {
1124 if(startChunk->previousHeader)
1125 {
1126 startChunk->previousHeader->nextHeader=endChunk->nextHeader;
1127 }
1128 else
1129 {
1130 universe->firstChunkHeader=endChunk->nextHeader;
1131 }
1132
1133 if(endChunk->nextHeader)
1134 {
1135 endChunk->nextHeader->previousHeader=startChunk->previousHeader;
1136 }
1137 else
1138 {
1139 universe->lastChunkHeader=startChunk->previousHeader;
1140 }
1141 startChunk->previousHeader=endChunk->nextHeader=NULL; // terminate the list
1142 }
1143
DeleteWithinChunk(TEXT_UNIVERSE * universe,CHUNK_HEADER * chunk,UINT32 offset,UINT32 numBytes,CHUNK_HEADER ** nextChunk,UINT32 * nextOffset)1144 static void DeleteWithinChunk(TEXT_UNIVERSE *universe,CHUNK_HEADER *chunk,UINT32 offset,UINT32 numBytes,CHUNK_HEADER **nextChunk,UINT32 *nextOffset)
1145 // data within one chunk is being deleted
1146 // NOTE: if the cache points to the given chunk, and it is completely deleted,
1147 // the cache will be moved forward to the next chunk in the list
1148 {
1149 UINT32
1150 linesDeleted;
1151
1152 if(numBytes<chunk->totalBytes) // see if something will remain after the deletion
1153 {
1154 DetermineNewLines(chunk,offset,numBytes,&linesDeleted);
1155 MMoveMem(&(chunk->data[offset+numBytes]),&(chunk->data[offset]),chunk->totalBytes-(offset+numBytes)); // move back any bytes needed
1156 chunk->totalBytes-=numBytes;
1157 chunk->totalLines-=linesDeleted;
1158 universe->totalBytes-=numBytes;
1159 universe->totalLines-=linesDeleted;
1160 if(offset>=chunk->totalBytes)
1161 {
1162 (*nextChunk)=chunk->nextHeader;
1163 (*nextOffset)=0;
1164 }
1165 else
1166 {
1167 (*nextChunk)=chunk;
1168 (*nextOffset)=offset;
1169 }
1170 }
1171 else
1172 {
1173 if(universe->cacheChunkHeader==chunk) // if cached chunk is going away, then just move it to the next one
1174 {
1175 universe->cacheChunkHeader=chunk->nextHeader;
1176 }
1177 (*nextChunk)=chunk->nextHeader;
1178 (*nextOffset)=0;
1179 UnlinkChunkList(universe,chunk,chunk);
1180 universe->totalBytes-=chunk->totalBytes;
1181 universe->totalLines-=chunk->totalLines;
1182 DisposeChunkList(chunk);
1183 }
1184 }
1185
DeleteUniverseText(TEXT_UNIVERSE * universe,UINT32 position,UINT32 numBytes)1186 bool DeleteUniverseText(TEXT_UNIVERSE *universe,UINT32 position,UINT32 numBytes)
1187 // delete numBytes of text from universe starting at position
1188 // if there is a problem, SetError, and return false
1189 // this routine is allowed to either succeed, or fail completely, under no circumstance
1190 // is it allowed to delete only part of the given text
1191 // NOTE: this will move the cache position to the chunk where the deletion started
1192 {
1193 UINT32
1194 numToDelete;
1195 CHUNK_HEADER
1196 *chunk;
1197 UINT32
1198 offset;
1199 bool
1200 fail;
1201
1202 fail=false;
1203 if(numBytes)
1204 {
1205 PositionToChunkPosition(universe,position,&chunk,&offset); // locate the position, move the cache there
1206 while(numBytes&&chunk)
1207 {
1208 if((chunk->totalBytes-offset)<numBytes)
1209 {
1210 numToDelete=chunk->totalBytes-offset;
1211 }
1212 else
1213 {
1214 numToDelete=numBytes;
1215 }
1216 DeleteWithinChunk(universe,chunk,offset,numToDelete,&chunk,&offset);
1217 numBytes-=numToDelete;
1218 }
1219 }
1220 return(!fail);
1221 }
1222
OpenTextUniverse()1223 TEXT_UNIVERSE *OpenTextUniverse()
1224 // create the data structures necessary to manage
1225 // a universe of text
1226 // if there is a problem, SetError, and return NULL
1227 {
1228 TEXT_UNIVERSE
1229 *universe;
1230
1231 if((universe=(TEXT_UNIVERSE *)MNewPtr(sizeof(TEXT_UNIVERSE)))) // create an empty universe
1232 {
1233 universe->firstChunkHeader=universe->lastChunkHeader=universe->cacheChunkHeader=(CHUNK_HEADER *)NULL;
1234 universe->totalBytes=0;
1235 universe->totalLines=0;
1236 universe->cacheBytes=0;
1237 universe->cacheLines=0;
1238 return(universe);
1239 }
1240 return((TEXT_UNIVERSE *)NULL);
1241 }
1242
CloseTextUniverse(TEXT_UNIVERSE * universe)1243 void CloseTextUniverse(TEXT_UNIVERSE *universe)
1244 // dispose of a text universe
1245 {
1246 DisposeChunkList(universe->firstChunkHeader);
1247 MDisposePtr(universe);
1248 }
1249
1250