home *** CD-ROM | disk | FTP | other *** search
- (* ------------------------------------------------------ *)
- (* REALLOC.PAS *)
- (* Mehr Dynamik für Turbo-Pascal 4.0/5.0/5.5 *)
- (* (c) 1991 Gerd Cebulla & TOOLBOX *)
- (* ------------------------------------------------------ *)
- UNIT ReAlloc;
-
- INTERFACE
-
- FUNCTION ChangeMem(P : Pointer;
- OldSize, NewSize : WORD) : Pointer;
-
- IMPLEMENTATION
-
- TYPE
- FreeRec = RECORD
- { Format eines Eintrags in der Fragmentliste }
-
- FreeStart,
- { Zeiger auf Anfang des freien Bereichs }
- FreeEnd : Pointer;
- { Zeiger auf erstes Byte oberhalb }
- { des freien Bereichs }
- END;
- FreeList = ARRAY [0..8190] OF FreeRec;
-
-
- FUNCTION AbsAddr(P : Pointer) : LongInt;
- { Wandelt einen Zeiger im Format Segment:Offset in eine }
- { absolute Adresse im Bereich 0..$FFFFF um. }
- BEGIN
- AbsAddr := LongInt(Seg(P^)) SHL 4 + Ofs(P^);
- END;
-
- FUNCTION NormPtr(Addr : LongInt) : Pointer;
- { Konvertiert absolute Adresse in einen "normalisierten" }
- { Zeiger, d.h., der Offsetanteil des Funktions- }
- { ergebnisses liegt immer zwischen 0 und 15. }
- BEGIN
- NormPtr := Ptr(Addr DIV 16, Addr MOD 16);
- END;
-
- PROCEDURE AddToFreeList(Start, Ende : Pointer);
- { Fügt einen neuen Eintrag in die Fragmentliste ein. }
- { "Start" zeigt auf den Anfang des freien Bereichs, }
- { "Ende" auf das erste Byte oberhalb. }
- VAR
- FP : ^FreeRec;
- BEGIN
- FP := Ptr(Seg(FreePtr^), Ofs(FreePtr^)-SizeOf(FreeRec));
- IF (Ofs(FP^) = 0) OR
- (AbsAddr(FP) < AbsAddr(HeapPtr)) THEN
- { Fragmentliste voll? }
- RunError(204); { "invalid pointer operation" }
- FP^.FreeStart := Start; { Werte eintragen }
- FP^.FreeEnd := Ende;
- FreePtr := FP; { FreePtr aktualisieren }
- END;
-
- PROCEDURE DeleteFromFreeList(DelP : Pointer);
- { Löscht den Eintrag, auf den DelP zeigt, aus der }
- { Fragmentliste. }
- VAR
- FP : ^FreeList;
- BEGIN
- IF DelP <> FreePtr THEN BEGIN
- FP := FreePtr;
- Move(FP^[0], FP^[1], AbsAddr(DelP)-AbsAddr(FreePtr));
- { Anfang der Fragmentliste nach oben verschieben }
- END;
- FreePtr := Ptr(Seg(FreePtr^),
- Ofs(FreePtr^) + SizeOf(FreeRec));
- { FreePtr aktualisieren }
- END;
-
- FUNCTION ChangeMem(P : Pointer;
- OldSize, NewSize : WORD) : Pointer;
- { Verändert die Größe des für die dynamische Variable P^ }
- { reservierten Speicherbereichs. Bei erfolgreicher }
- { Ausführung zeigt das Funktionsergebnis auf die neue }
- { Adresse der dynamischen Variable, ansonsten wird der }
- { Wert NIL zurückgeliefert. }
- VAR
- NewP, { neue Adresse der dyn. Var. }
- OldPEnd, { alte Endadresse der dyn. Var. }
- NewPEnd : Pointer; { neue Endadresse der dyn. Var. }
- FP : ^FreeList; { Zeiger auf Fragmentliste }
- FPred, { Zeiger auf Fragmentlisteneintrag }
- { für freien Speicherplatz unterhalb von P^ }
- FSucc : ^FreeRec; { dto. oberhalb von P^ }
- Frei : LongInt; { verfügbarer Speicherplatz }
- FIndex, { Index eines Fragmentlisteneintrags }
- FAnzahl, { Anzahl der Fragmentlisteneinträge }
- FLen : WORD; { Länge der Fragmentliste in Byte }
- RetCode : INTEGER; { Rückgabewert HeapError-Funktion }
- Fehler : BOOLEAN;{ True, wenn nicht genug freier Heap }
- BEGIN
- IF (OldSize = 0) AND (NewSize = 0) THEN
- ChangeMem := NIL
- ELSE IF OldSize = 0 THEN BEGIN
- GetMem(P, NewSize); { dyn. Var. neu anlegen }
- ChangeMem := P;
- END ELSE IF NewSize = 0 THEN BEGIN
- FreeMem(P, OldSize); { dyn. Var. löschen }
- ChangeMem := NIL;
- END ELSE BEGIN
- P := NormPtr(AbsAddr(P)); { P normalisieren }
- OldPEnd := NormPtr(AbsAddr(P) + OldSize);
- REPEAT
- Fehler := FALSE;
- IF (LongInt(P) < LongInt(HeapOrg)) OR
- (LongInt(OldPEnd) > LongInt(HeapPtr)) THEN
- { zeigt P auf eine Adresse außerhalb des Heap? }
- RunError(204); { "invalid pointer operation" }
- IF OldSize = NewSize THEN
- { Größe von P^ bleibt gleich? }
- ChangeMem := P { dann fertig }
- ELSE BEGIN
- IF Ofs(FreePtr^) = 0 THEN
- FAnzahl := 0
- ELSE
- FAnzahl := ($10000 - Ofs(FreePtr^)) DIV
- SizeOf(FreeRec);
- FIndex := 0;
- FPred := NIL;
- FSucc := NIL;
- FP := FreePtr; { FP zeigt auf Fragmentliste }
- WHILE (FIndex < FAnzahl) AND
- ((FPred = NIL) OR (FSucc = NIL)) DO BEGIN
- { Fragmentliste durchsuchen }
- IF FP^[FIndex].FreeStart = OldPEnd THEN
- FSucc := @FP^[FIndex]
- { freier Speicher oberhalb von P^ }
- ELSE IF FP^[FIndex].FreeEnd = P THEN
- FPred := @FP^[FIndex];
- { freier Speicher unterhalb von P^ }
- Inc(FIndex);
- END;
- IF NewSize < OldSize THEN BEGIN
- { den für P^ reservierten Speicher verkleinern }
- NewPEnd := NormPtr(AbsAddr(P) + NewSize);
- IF HeapPtr = OldPEnd THEN
- HeapPtr := NewPEnd
- ELSE IF FSucc <> NIL THEN
- FSucc^.FreeStart := NewPEnd
- ELSE
- AddToFreeList(NewPEnd, OldPEnd);
- ChangeMem := P;
- END ELSE BEGIN
- { freien Speicherplatz oberhalb von P^ berechnen }
- IF HeapPtr = OldPEnd THEN BEGIN
- FLen := FAnzahl * SizeOf(FreeRec);
- IF FreeMin > FLen THEN
- FLen := FreeMin;
- Frei := LongInt(Seg(FreePtr^) + $1000) SHL 4 -
- FLen - AbsAddr(HeapPtr);
- END ELSE IF FSucc = NIL THEN
- Frei := 0
- ELSE
- Frei := AbsAddr(FSucc^.FreeEnd) -
- AbsAddr(FSucc^.FreeStart);
- Inc(Frei, OldSize);
- { den von P^ belegten Speicherplatz addieren }
- IF Frei >= NewSize THEN BEGIN
- { genug Platz oberhalb von P^ ? }
- { dann entsprechend Speicher reservieren }
- NewPEnd := NormPtr(AbsAddr(P) + NewSize);
- IF HeapPtr = OldPEnd THEN
- HeapPtr := NewPEnd
- ELSE IF Frei = NewSize THEN
- DeleteFromFreeList(FSucc)
- ELSE
- FSucc^.FreeStart := NewPEnd;
- ChangeMem := P;
- END ELSE BEGIN
- { freien Speicher unterhalb von P^ ermitteln }
- IF FPred <> NIL THEN
- Inc(Frei, AbsAddr(FPred^.FreeEnd) -
- AbsAddr(FPred^.FreeStart));
- IF Frei >= NewSize THEN BEGIN
- { genug Platz unterhalb von P^ ? }
- { dann P^ nach unten verschieben }
- { und Fragmentliste aktualisieren }
- NewP := FPred^.FreeStart;
- NewPEnd := NormPtr(AbsAddr(NewP) + NewSize);
- Move(P^, NewP^, OldSize);
- DeleteFromFreeList(FPred);
- IF HeapPtr = OldPEnd THEN
- HeapPtr := NewPEnd
- ELSE IF FSucc <> NIL THEN
- IF Frei = NewSize THEN
- DeleteFromFreeList(FSucc)
- ELSE
- FSucc^.FreeStart := NewPEnd
- ELSE IF NewPEnd <> OldPEnd THEN
- AddToFreeList(NewPEnd, OldPEnd);
- ChangeMem := NewP;
- END ELSE BEGIN
- { weder ober- noch unterhalb }
- { von P^ genug Platz ? }
- { dann anderswo Speicher reservieren, }
- { P^ kopieren und ursprünglichen }
- { Speicherbereich freigeben }
- GetMem(NewP, NewSize);
- IF NewP <> NIL THEN BEGIN
- Move(P^, NewP^, OldSize);
- FreeMem(P, OldSize);
- ChangeMem := NewP;
- END ELSE
- Fehler := TRUE; { nicht genug Heap frei }
- END;
- END;
- END;
- END;
- IF Fehler THEN BEGIN
- IF HeapError = NIL THEN
- { keine Heapfehlerbehandlung installiert? }
- RunError(203) { "heap overflow error" }
- ELSE BEGIN { sonst benutzerdefinierte }
- { Heapfehlerroutine aufrufen }
- INLINE (
- $FF/$B6/NewSize/ { push NewSize[bp] }
- $FF/$1E/HeapError/ { call far [HeapError] }
- $89/$86/RetCode { mov RetCode[bp],ax }
- );
- IF RetCode = 0 THEN
- RunError(203) { "heap overflow error" }
- ELSE IF RetCode = 1 THEN BEGIN
- Fehler := FALSE;
- ChangeMem := NIL;
- END;
- END;
- END;
- UNTIL NOT Fehler;
- END;
- END;
-
- END.
- (* ------------------------------------------------------ *)
- (* Ende von REALLOC.PAS *)