{*********************************************************} {* VMM.IN2 1.00 *} {*********************************************************} constructor Dynarray.Init(MaxElements, ElementSize, Incr : Word); {-Called when a dynamic array is created} begin if (not Root.init) then Fail; if (ElementSize = 0) or (Incr = 0) or (Incr > MaxElements) or (MaxElements = 0) or (LongInt(ElementSize)*Incr > MaxHeapAlloc) or (LongInt(MaxElements)*ElementSize > MaxHeapAlloc) then begin Done; InitStatus := epFatal+ecBadParam; Fail; end; daBase := nil; daElemSize := ElementSize; daArraySize:= 0; daInc := Incr; daMaxIndex := MaxElements-1; daValidElems := 0; daStatus := 0; end; destructor DynArray.Done; {-Free memory occupied by the array} begin Clear; Root.Done; end; function DynArray.GetStatus : Word; {-Return and reset array status} begin GetStatus := daStatus; daStatus := 0; end; function DynArray.PeekStatus : Word; {-Return array status} begin PeekStatus := daStatus; end; procedure DynArray.Error(Code : Word); {-Assign error code} begin daStatus := Code; end; procedure DynArray.SetElem(Index : Word; var Elem); {-Set an array element to a given value; Increase size if necessary} var P : Pointer; NewSize : Word; NeededElems : Word; begin if Index > daMaxIndex then begin Error(epFatal+ecBadParam); Exit; end; NeededElems := Succ(Index); if NeededElems > daArraySize div daElemSize then begin {The memory space allocated to the array must be increased} if (NeededElems mod daInc <> 0) or (Index = 0) then NeededElems := Succ(NeededElems div daInc) * daInc; if NeededElems > Succ(daMaxIndex) then NeededElems := Succ(daMaxIndex); {No superfluous allocation} NewSize := NeededElems*daElemSize; if UserGetMem(P, NewSize) then begin FillChar(AddWordToPtr(P, daArraySize)^, NewSize-daArraySize, 0); Move(daBase^, P^, daArraySize); {Data transfer} UserFreeMem(daBase, daArraySize); {The bigger daIncr, the lesser} daArraySize := NewSize; { the heap will be fragmented} daBase := P; end else begin Error(epFatal+ecOutOfMemory); Exit; end; end; if Succ(Index) > daValidElems then daValidElems := Succ(Index); {Now stores the element data into the array} Move(Elem, AddWordToPtr(daBase, daElemSize*Index)^, daElemSize); end; procedure DynArray.GetElem(Index : Word; var Elem); {-Return the indexth element} begin if Succ(LongInt(Index)) > daValidElems then Error(epFatal+ecBadParam) else Move(AddWordToPtr(daBase, daElemSize*Index)^, Elem, daElemSize); end; function DynArray.GetElemSize : Word; {-Return size of an element} begin GetElemSize := daElemSize; end; function DynArray.GetArraySize : Word; {-Return actual size of array} begin GetArraySize := daArraySize; end; function DynArray.GetMaxIndex : Word; {-Return maximum index allowed} begin GetMaxIndex := daMaxIndex; end; function DynArray.GetValidElems : Word; {-Return number of valid elements} begin GetValidElems := daValidElems; end; procedure DynArray.Shrink(ElemNb : Word); {-Shrink array size to ElemNb elements and discard exceeding elements} var P : pointer; NewSize : Word; SaveElemNb : Word; begin if ElemNb = 0 then begin Clear; Exit; end; if ElemNb >= daArraySize div daElemSize then Exit; SaveElemNb := ElemNb; if ElemNb mod daInc <> 0 then ElemNb := Succ(ElemNb div daInc) * daInc; NewSize := ElemNb*daElemSize; if NewSize < daArraySize then {Need to reallocate a smaller buffer} if UserGetMem(P, NewSize) then begin Move(daBase^, P^, NewSize); {No need to fill with nulls since} UserFreeMem(daBase, daArraySize); { it's a smaller block} daArraySize := NewSize; daBase := P; end else begin Error(epFatal+ecOutOfMemory); Exit; end; {No reallocation - just need to adjust daValidElems} if daValidElems > SaveElemNb then daValidElems := SaveElemNb; end; procedure DynArray.Clear; {-Reset array to minimum size and discard all elements} begin UserFreeMem(daBase, daArraySize); daArraySize := 0; daValidElems := 0; daStatus := 0; end; constructor DynArray.Load(var S : IdStream); {-Load a dynamic array from a stream} begin daBase := nil; if not Root.Init then Fail; {Read characteristics of dynamic array} S.ReadRange(daElemSize, daBase); if S.PeekStatus <> 0 then begin Done; Fail; end; {Allocates memory to store array data} if not UserGetMem(daBase, daArraySize) then begin Done; InitStatus := epFatal+ecOutOfMemory; Fail; end; {Now read array data} S.Read(daBase^, daArraySize); if S.PeekStatus <> 0 then begin Done; Fail; end; end; procedure DynArray.Store(var S : IdStream); {-Store a dynamic array in a stream} begin {Write characteristics of dynamic array} {Only daBase is not stored} S.WriteRange(daElemSize, daBase); {Write array data} S.Write(daBase^, daArraySize); end; procedure DynArrayStream(SPtr : IdStreamPtr); {-Register all types needed for streams containing DynArrays} begin SPtr^.RegisterType(otDynArray, veDynArray, TypeOf(DynArray), @DynArray.Store, @DynArray.Load); end; {---------------------------------------------------------------------} procedure VmmStaticQueue.Remove(var Element); {-Remove first element found equal to Element from the queue} { This procedure is needed to maintain the LRU queue. The very nature of the LRU algorithm is to push into the queue a VMM handle each time it is dereferenced. So, if we make sure that this handle is deleted before pushing it into the queue, when we lock it or when we free it, we'll also be sure that the "Least Recently Used" handle will be the first one to be popped out from the queue. Since we are sure that the elements processed by a VmmStaticQueue are always handles (i.e. WORDs) the CompElem function is not really needed because we should only compare WORDs. Though, the CompElem function allows the VmmStaticQueue to be used for other purposes. } var Ptr : Word; Found : Boolean; begin if sqTail > sqHead then begin {There is no wrap-around in the queue} Ptr := sqHead; Found := false; while not Found and (Ptr < sqTail) do begin Inc(Ptr, sqElSize); Found := CompElem(Element, sqBase^[Ptr], sqElSize); end; if Found then begin {Remove element} Move(sqBase^[Ptr+sqElSize], sqBase^[Ptr], sqTail-Ptr); sqDec(sqTail); end; end else if not IsEmpty then begin {First search from Head to end of buffer} Ptr := sqHead; Found := false; while not Found and (Ptr < sqSize) do begin Inc(Ptr, sqElSize); Found := CompElem(Element, sqBase^[Ptr], sqElSize); end; if not Found then begin {Search from beginning of buffer to Tail} Ptr := 0; repeat Found := CompElem(Element, sqBase^[Ptr], sqElSize); Inc(Ptr, sqElSize); until Found or (Ptr >= sqTail); Dec(Ptr, sqElSize); end; if Found then begin {Remove element} if (Ptr > sqHead) then begin {A little bit trickier in that case - circular move} Move(sqBase^[Ptr+sqElSize], sqBase^[Ptr], (sqSize-Ptr-sqElSize)); Move(sqBase^, sqBase^[sqSize-sqElSize], sqElSize); Move(sqBase^[sqElSize], sqBase^, sqTail); end else Move(sqBase^[Ptr+sqElSize], sqBase^[Ptr], sqTail-Ptr); sqDec(sqTail); end; end; {If not found does nothing} end; function VmmStaticQueue.IsEmpty : Boolean; {-Return true if queue is empty} begin IsEmpty := sqHead = sqTail; end; {---------------------------------------------------------------------} constructor AbstractFreeList.Init(MaxElements, Incr : Word); {-Initialize a dynamic array of FreeRecords} begin if not DynArray.Init(MaxElements, SizeOf(FreeRecord), Incr) then Fail; end; function AbstractFreeList.GetFreeEntrySize(Index : Word) : LongInt; {-Return size of a free block} begin {This virtual method must be overridden by descendants} Abstract; end; function AbstractFreeList.SizeToEndPtr(OrgPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr and block size, return new entry's EndPtr} begin {This virtual method must be overridden by descendants} Abstract; end; function AbstractFreeList.SizeToOrgPtr(EndPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr, EndPtr and block size, return new entry's OrgPtr} begin {This virtual method must be overridden by descendants} Abstract; end; function AbstractFreeList.PtrIsEqual(P1, P2 : Pointer) : Boolean; {-Return true if pointers can be merged to form a new freelist entry} begin {This virtual method must be overridden by descendants} Abstract; end; function AbstractFreeList.GetFreeEntry(BlkSize : Word) : Pointer; {-Search free list for a free block, return a pointer to it} var CurIndex : Word; CurEntSize : LongInt; CurFreeRec : FreeRecord; begin if daValidElems = 0 then begin GetFreeEntry := nil; Exit; end else begin for CurIndex := 0 to Pred(daValidElems) do begin {Scan free list for a block that is big enough} GetElem(CurIndex, CurFreeRec); if GetStatus <> 0 then begin GetFreeEntry := nil; Exit; end; CurEntSize := GetFreeEntrySize(CurIndex); if CurEntSize > BlkSize then begin {bigger than needed - shrink size of block} GetFreeEntry := CurFreeRec.OrgPtr; CurFreeRec.OrgPtr := SizeToOrgPtr(CurFreeRec.EndPtr, CurEntSize-BlkSize); SetElem(CurIndex, CurFreeRec); if (GetStatus = 0) and Sort then; {Sort free list to make sure GetFreeEntry will always choose the} { smallest possible block - this will prevent fragmentation} Exit; end else if CurEntSize = BlkSize then begin {Exact match} GetFreeEntry := CurFreeRec.OrgPtr; {Delete used entry} RemoveFreeEntry(CurIndex); if (GetStatus = 0) and Sort then; Exit; end; end; {We didn't find a free entry which size is >= BlkSize} GetFreeEntry := nil; end; end; function AbstractFreeList.AddFreeEntry(ThisOrgP : Pointer; BlkSize : LongInt) : LongInt; {-Insert a new free block in the FreeList or merge it with an } { existing one - return size of entry in FreeList} var SaveIndex : Word; CurIndex : Word; CurFreeRec : FreeRecord; ThisEndP : Pointer; FoundOne : Boolean; FoundTwo : Boolean; Found : Boolean; Pass : 1..2; label AddIt; begin ThisEndP := SizeToEndPtr(ThisOrgP, BlkSize); FoundOne := false; FoundTwo := false; if daValidElems = 0 then {Nothing to search for} Goto AddIt; for Pass := 1 to 2 do begin {All blocks combinations should be found in two passes} CurIndex := 0; Found := false; while (CurIndex <= Pred(daValidElems)) and not Found do begin {search for a free list entry to combine with} GetElem(CurIndex, CurFreeRec); {does the EndPtr of our entry match the start of the current one ?} if PtrIsEqual(ThisEndP, CurFreeRec.OrgPtr) then begin CurFreeRec.OrgPtr := ThisOrgP; Found := true; {Save index for freelist update if second match found} if Pass = 1 then begin ThisEndP := CurFreeRec.EndPtr; {save it for next loop} SaveIndex := CurIndex; FoundOne := true; end else {Second match found} FoundTwo := true; end {does the OrgPtr of our entry match the ind of the current one ?} else if PtrIsEqual(ThisOrgP, CurFreeRec.EndPtr) then begin CurFreeRec.EndPtr := ThisEndP; Found := true; if Pass = 1 then begin ThisOrgP := CurFreeRec.OrgPtr; {save it for next loop} SaveIndex := CurIndex; FoundOne := true; end else FoundTwo := true; end; {go to next entry in the freelist or...} if not Found then Inc(CurIndex) else begin {...update entry in freeList} SetElem(CurIndex, CurFreeRec); if GetStatus <> 0 then AddFreeEntry := 0 else AddFreeEntry := GetFreeEntrySize(CurIndex); end; end; end; AddIt: if FoundTwo then {We found two blocks to combine with ours - the first one has to be deleted} RemoveFreeEntry(SaveIndex) else if not FoundOne then begin {No block combination was possible - add new entry to freelist} CurFreeRec.OrgPtr := ThisOrgP; CurFreeRec.EndPtr := SizeToEndPtr(ThisOrgP, BlkSize); SetElem(daValidElems, CurFreeRec); AddFreeEntry := GetFreeEntrySize(Pred(daValidElems)); end; if not ((GetStatus = 0) and Sort) then AddFreeEntry := 0; {Sort free list to make sure GetFreeEntry will always choose the} { smallest possible block - this will prevent fragmentation} end; procedure AbstractFreeList.RemoveFreeEntry(Index : Word); {-Remove entry from the list and shrink list size} var LastIndex : Word; F : FreeRecord; begin if (daValidElems = 0) or (Index > daValidElems) then begin Error(epFatal+ecBadParam); Exit; end; LastIndex := Pred(daValidElems); {Move last entry...} GetElem(LastIndex, F); {...to the entry to be deleted} SetElem(Index, F); {and shrink freelist by one element} Shrink(LastIndex); end; function AbstractFreeList.MaxFree : Longint; {-Return size of largest free entry} begin {Since the free list is always sorted in block size order} { the largest block available is always the last one} if daValidElems > 0 then MaxFree := GetFreeEntrySize(Pred(daValidElems)) else MaxFree := 0; end; procedure AbstractFreeList.QuickSort(L, R : Word); {-Actual sort procedure called by Sort} const StackToKeep = 512; var i, j, p : LongInt; Ei, Ej : FreeRecord; begin if SPtr > StackToKeep then begin {Keep StackToKeep bytes free on stack} i := L; {Each recursion uses approximately 50 bytes} j := R; p := (i+j) div 2; repeat while GetFreeEntrySize(i) < GetFreeEntrySize(p) do Inc(i); while GetFreeEntrySize(p) < GetFreeEntrySize(j) do Dec(j); if i <= j then begin {Swap elements} GetElem(i, Ei); GetElem(j, Ej); SetElem(i, Ej); SetELem(j, Ei); Inc(i); Dec(j); end; until i > j; if L < j then QuickSort(L, j); {Recursive call with new boundaries} IF i < R then QuickSort(i, R); end else Error(epNonFatal+ecOutOfMemory); end; function AbstractFreeList.Sort : boolean; {-Sort the free list in block size order} var Count : Word; const MaxCount = 3; begin Count := 0; if daValidElems > 1 then repeat QuickSort(0, Pred(daValidElems)); Inc(Count); until (PeekStatus = 0) or (Count = MaxCount); Sort := GetStatus = 0; { Some explanations needed here. It's very important for the VMM that the freelist sort succeeds. If not, MaxFree will not return the right value and fragmentation will begin. The only reason for Sort to fail is that we could run out of stack space. In that case the array remains partially sorted. However, the number of recursions needed for a QuickSort depends heavily on the initial order of items in the array. So, a second (or a third) try on the partially sorted array may (will likely) succeed. Moreover, freelists are sorted very often. Hence, the required number of recursions will be very low. In most cases freelists will not be very big. So the SORT method will succeed anyway. Some experiments showed that even very big arrays can be sorted in 3 passes. In the very rare situations where 3 passes are not enough, you may want to increase MaxCount to allow more passes. } end; {---------------------------------------------------------------------} function VmmRamFreeList.GetFreeEntrySize(Index : Word) : LongInt; {-Return size of a free block} var F : FreeRecord; begin GetElem(Index, F); if GetStatus = 0 then with F do GetFreeEntrySize := PtrToLong(EndPtr) - PtrToLong(OrgPtr) else GetFreeEntrySize := 0; end; function VmmRamFreeList.SizeToEndPtr(OrgPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr and block size, return new entry's EndPtr} begin {Assume BlkSize validity} SizeToEndPtr :=AddLongToPtr(OrgPtr, BlkSize); end; function VmmRamFreeList.SizeToOrgPtr(EndPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given EndPtr and block size, return new entry's OrgPtr} begin {Assume BlkSize validity} SizeToOrgPtr := LongToPtr(PtrToLong(EndPtr) - BlkSize); end; function VmmRamFreeList.PtrIsEqual(P1, P2 : Pointer) : Boolean; {-Return true if pointers can be merged to form a new freelist entry} begin PtrIsEqual := PtrToLong(P1) = PtrToLong(P2); end; {---------------------------------------------------------------------} function VmmEmsFreeList.AddFreeEntry(ThisOrgP : Pointer; BlkSize : Word) : LongInt; {Override generic AddFreeEntry method because Ems need special handling} { This method will deallocate an Ems frame when it is empty} var F : FreeRecord; Found : Boolean; CurIndex : Word; begin {Use generic method and if entire Ems page frame is free, deallocate handle} if AbstractFreeList.AddFreeEntry(ThisOrgP, BlkSize) = MaxEmsBlock then begin {Because the freelist has been sorted in block order size the new} { entry is now necessarily the last one because it has the maximum} { size - So we only have to free the handle of the last entry and} { to remove it from the list} GetElem(Pred(daValidElems), F); if not DeAllocateEmsHandle(VmmPtrRec(F.OrgPtr).Seg) then Error(epNonFatal+ecCantFreeEms) else begin {Remove entry from freelist - we remove the last one, no need to sort} RemoveFreeEntry(Pred(daValidElems)); if PeekStatus <> 0 then AddFreeEntry := 0; end; end; end; function VmmEmsFreeList.GetFreeEntrySize(Index : Word) : LongInt; {-Return size of a free block} var F : FreeRecord; begin GetElem(Index, F); if GetStatus = 0 then with F do GetFreeEntrySize := VmmPtrRec(EndPtr).Ofs - VmmPtrRec(OrgPtr).Ofs else GetFreeEntrySize := 0; {The segment part is assumed to be the same for EndPtr and OrgPtr} { It is the Ems handle - a free entry in EmsFreeList cannot be > 64k} end; function VmmEmsFreeList.SizeToEndPtr(OrgPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr and block size, return new entry's EndPtr} begin {Assume BlkSize validity - entries cannot be greater than 64k} Inc(VmmPtrRec(OrgPtr).Ofs, Word(BlkSize)); SizeToEndPtr := OrgPtr; {The segment part is assumed to be the same for EndPtr and OrgPtr} { It is the Ems handle} end; function VmmEmsFreeList.SizeToOrgPtr(EndPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr, EndPtr and block size, return new entry's OrgPtr} begin {Assume BlkSize validity - entries cannot be greater than 64k} Dec(VmmPtrRec(EndPtr).Ofs, Word(BlkSize)); SizeToOrgPtr :=EndPtr; {The segment part is assumed to be the same for EndPtr and OrgPtr} { It is the Ems handle} end; function VmmEmsFreeList.PtrIsEqual(P1, P2 : Pointer) : Boolean; {-Return true if pointers can be merged to form a new freelist entry} begin PtrIsEqual := P1 = P2; {Segment (handle) and offset must be the same} end; {---------------------------------------------------------------------} function VmmDskFreeList.GetFreeEntrySize(Index : Word) : LongInt; {-Return size of a free block} var F : FreeRecord; Offsets : array [1..2] of LongInt absolute F; begin GetElem(Index, F); if GetStatus = 0 then GetFreeEntrySize := Offsets[2] - Offsets[1] else GetFreeEntrySize := 0; end; function VmmDskFreeList.SizeToEndPtr(OrgPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr and block size, return new entry's EndPtr} var BlockOrg : LongInt absolute OrgPtr; begin SizeToEndPtr := Pointer(BlockOrg + BlkSize); end; function VmmDskFreeList.SizeToOrgPtr(EndPtr : Pointer; BlkSize : LongInt) : Pointer; {-Given OrgPtr, EndPtr and block size, return new entry's OrgPtr} var BlockEnd : LongInt absolute EndPtr; begin SizeToOrgPtr := Pointer(BlockEnd - BlkSize); end; function VmmDskFreeList.PtrIsEqual(P1, P2 : Pointer) : Boolean; {-Return true if pointers can be merged to form a new freelist entry} begin PtrIsEqual := P1 = P2; {LongInt(P1) = LongInt(P2)} end;