-
Хочу поставить маленький эксперимент по привлечению обитателей "Мастаков" в ряды сообщества OpenSource :^). Итак, имеется баг в компиляторе, нужен хороший багфикс (мой оказался дефективным). Суть проблемы. Как известно, на некоторых платформах, включая Win32, экспорт и импорт процедур и функций возможен не только по именам, но и по целочисленным индексам. До недавнего времени при компиляции раздела Exports компилятор FreePascal, прочитав очередное имя экспортируемого элемента, немедленно генерил платформо-зависимый код. Если элемент имел индекс, который был больше предшествующего, индекс экспортировался и запоминался, как пердшествующий для следующего. Такая схема нормально работала, только если индексы следовали в секции Exports в возрастающем порядке. Если порядок нарушался или индексы имелись не у всех элементов, начинались ложные срабатывания на повторный индекс. Я предложил сначала делать связанный список экспортируемых элементов, сортировать его и лишь потом экспортировать. Однако путь этот не очень хорош, поскольку список может отъедать много памяти, а реально индексы используются не так уж часто. Если есть идеи, готов пообсуждать.
-
Хочу помочь, но чего-то формулировки не понял. :-)
-
2pasha_golub: До последнего багфикса при компиляции подобного: exports proc1 index 2, proc2 index 1; компилятор выдавал сообщение об ошибке. Сейчас эта проблема решена, однако вот такое скомпилировать по-прежнему невозможно: exports proc1, proc2 index 1; Хотелось бы придумать разумное решение проблемы. Подробности об исходном тексте дам позднее (сейчас я нахожусь не там, где у меня он есть :^)).
-
proc1 должно иметь индекс или нет?
-
Сейчас если все экспортируемые модули имеют индексы (или все не имеют), всё ОК. А вот если смесь индексированных и не индексированных (как во 2-м примере), возникает трабл. Ординал в PECOFF-файле все равно нужен, просто либо компилятор генерит его автоматически, либо испрользует значение индекса.
-
А у меня тут вота вопрос такой ламерский. Компилятор Паскаль он типа однопроходный, это то есть за одно прочтение текста модуля он его построить должон? Если за несколько проходов, то можно вроде.
-
> Я предложил сначала делать связанный список экспортируемых > элементов, сортировать его и лишь потом экспортировать. > Однако путь этот не очень хорош, поскольку список может > отъедать много памяти, а реально индексы используются не > так уж часто.
Нет, мне это непонятно... Там где индексы используются редко, там и список не отъест много памяти, а там где часто - там столько, сколько надо, столько и отъест. На то он и список. И в текщей реализации же где-то запоминается - какой индекс уже использован, а какой нет. Ну не хочется списком, можно сортированным дин. массивом, будет меньше кушать памяти, зато тормознутее будет. Кстати, если учесть, что этот массив в принципе только увеличивается в размере, то и не так уж тормознуто будет. И вообще, что значит "много"?
-
>И в текщей реализации же где-то запоминается - какой индекс уже использован, а какой нет. В предшествующих версиях шло лишь сравнение, больше ли обрабатываемый индекс предыдущего.
>Ну не хочется списком, можно сортированным дин. массивом, будет >меньше кушать памяти, зато тормознутее будет. Кстати, если учесть, > что этот массив в принципе только увеличивается в размере, то и >не так уж тормознуто будет.
А есть ли уверенность, что изменение объема выделенного блока памяти в динамическом массиве не сопровождается скрытым временным выделением нового блока с последующим копированием и освобождением старого? И не будет ли расход памяти из-за этого только хуже?
-
А можно дин. массив, а чтобы кушало медленнее работать побитово.
-
может я совсем дуб, но не вижу проблем.. Запоминает (не обязательно в дин массиве) индексы, как встретили экспортируемую функцию с индексом - ищем этот индекс - нашли -> ошибка | не нашли -> добавляем индекс массив.. Варианты реализации будут зависить от того на какое кол-во экспорт. функций оптимизировать..
> А есть ли уверенность, что изменение объема выделенного > блока памяти в динамическом массиве не сопровождается скрытым > временным выделением нового блока с последующим копированием > и освобождением старого? А возможны другие варианты? Просто разом длину массива побольше выставить. Я так понял - на это Verg намекает..
-
> А есть ли уверенность, что изменение объема выделенного > блока памяти в динамическом массиве не сопровождается скрытым > временным выделением нового блока с последующим копированием > и освобождением старого? И не будет ли расход памяти из-за > этого только хуже?
Абсолютно уверен (знаю), что сопровождается "скрытым временным" ипроч. Но почему это так смущает? Ну не надо приращать размер выделенной памяти под массив (reallocmem) на каждую добавленную к нему запись, добавляйте с неким Delta (5-10-20).
Расход памяти будет только меньше и "потряхиваний кучи" тоже, т.к. связный список хорош тогда, когда идет постояннное и пополнение и удаление из него, а массив хорош - во всех остальных случаях :)
С сортированным (сортирующимся) массивом будет больше move-есов (перемещений его кусманов)
-
Пока мне кажется, что TSortedCollection (Objects.pas) - оптимальная реализация. Памяти чуть больше, чем на просто дин. массив, но зато намного меньшие куски памяти перемещаются при вставках.
-
2Verg[11]: Принял к сведению, посмотрю.
-
Кажется, не судьба использовать TSortedCollection из Objects.pas... Потому что нынешние исходники FreePascal, ориентированные на компиляцию самим компилятором FreePascal, "вычищены" от объектов синтаксиса BP и переведены на классы в стиле Delphi. Думаю, возврат к objects будет воспринят в штыки. Еще вопрос: а как быть, если target в принципе не поддерживает индексы. Компилятор-то тот же самый. Что дешевле: ввести в описание каждой платформы поле IndexSupport или маятся со списками/массвами и там, где можно обойтись одним проходом?
-
> Думаю, возврат к objects будет воспринят в штыки.
Тогда я не понимаю... Этот TSortedCollection - вещь тривиальная. 20 минут на портацию в "стиль дельфи" и 40 минут - на убирание комментариев для того, чтобы сюда влезло
unit Collection;
interface
uses SysUtils;
CONST
coIndexError = -1;
coOverflow = -2;
MaxCollectionSize = 65536;
type
Sw_Integer = integer;
EFPCollectionError = class(Exception);
TCollection = class
Items: array of pointer;
Count: Sw_Integer;
Limit: Sw_Integer;
Delta: Sw_Integer;
CONSTRUCTOR Create (ALimit, ADelta: Sw_Integer);
DESTRUCTOR destroy; override;
FUNCTION At (Index: Sw_Integer): Pointer;
FUNCTION IndexOf (Item: Pointer): Sw_Integer; Virtual;
PROCEDURE Pack;
PROCEDURE FreeAll;
PROCEDURE DeleteAll;
PROCEDURE Free (Item: Pointer);
PROCEDURE Insert (Item: Pointer); Virtual;
PROCEDURE Delete (Item: Pointer);
PROCEDURE AtFree (Index: Sw_Integer);
PROCEDURE FreeItem (Item: Pointer); Virtual;
PROCEDURE AtDelete (Index: Sw_Integer);
PROCEDURE SetLimit (ALimit: Sw_Integer); Virtual;
PROCEDURE Error (Code, Info: Integer); Virtual;
PROCEDURE AtPut (Index: Sw_Integer; Item: Pointer);
PROCEDURE AtInsert (Index: Sw_Integer; Item: Pointer);
END;
TYPE
TSortedCollection = class (TCollection)
Duplicates: Boolean;
CONSTRUCTOR Create (ALimit, ADelta: Sw_Integer);
FUNCTION KeyOf (Item: Pointer): Pointer; Virtual;
FUNCTION IndexOf (Item: Pointer): Sw_Integer; Virtual;
FUNCTION Compare (Key1, Key2: Pointer): Sw_Integer; Virtual;
abstract;
FUNCTION Search (Key: Pointer; Var Index: Sw_Integer): Boolean;Virtual;
PROCEDURE Insert (Item: Pointer); Virtual;
END;
implementation
CONSTRUCTOR TCollection.Create(ALimit, ADelta: Sw_Integer);
BEGIN
Inherited Create;
Delta := ADelta;
SetLimit(ALimit);
END;
DESTRUCTOR TCollection.Destroy;
BEGIN
FreeAll;
SetLimit(0);
inherited;
END;
FUNCTION TCollection.At (Index: Sw_Integer): Pointer;
BEGIN
If (Index < 0) OR (Index >= Count) Then Begin
Error(coIndexError, Index);
At := Nil;
End Else At := Items[Index];
END;
FUNCTION TCollection.IndexOf (Item: Pointer): Sw_Integer;
VAR I: Sw_Integer;
BEGIN
If (Count>0) Then Begin
For I := 0 To Count-1 Do
If (Items[I]=Item) Then Begin
IndexOf := I;
Exit;
End;
End;
IndexOf := -1;
END;
PROCEDURE TCollection.Pack;
VAR I, J: Sw_Integer;
BEGIN
I := 0;
J := 0;
While (I<Count) AND (J<Limit) Do Begin
If (Items[J]<>Nil) Then Begin
If (I<>J) Then Begin
Items[I] := Items[J];
Items[J] := Nil;
End;
Inc(I);
End;
Inc(J);
End;
If (I<Count) Then Count := I;
END;
PROCEDURE TCollection.FreeAll;
VAR I: Sw_Integer;
BEGIN
for I := Count-1 downto 0 do
FreeItem(At(I));
Count := 0;
END;
PROCEDURE TCollection.DeleteAll;
BEGIN
Count := 0;
END;
PROCEDURE TCollection.Free (Item: Pointer);
BEGIN
Delete(Item);
FreeItem(Item);
END;
PROCEDURE TCollection.Insert (Item: Pointer);
BEGIN
AtInsert(Count, Item);
END;
PROCEDURE TCollection.Delete (Item: Pointer);
BEGIN
AtDelete(IndexOf(Item));
END;
PROCEDURE TCollection.AtFree (Index: Sw_Integer);
VAR Item: Pointer;
BEGIN
Item := At(Index);
AtDelete(Index);
FreeItem(Item);
END;
PROCEDURE TCollection.FreeItem (Item: Pointer);
BEGIN
TObject(Item).Free;
END;
PROCEDURE TCollection.AtDelete (Index: Sw_Integer);
BEGIN
If (Index >= 0) AND (Index < Count) Then Begin
Dec(Count);
If (Count>Index) Then
Move(Items[Index+1],
Items[Index],
(Count-Index)*Sizeof(Pointer));
End Else Error(coIndexError, Index);
END;
PROCEDURE TCollection.SetLimit (ALimit: Sw_Integer);
BEGIN
If (ALimit < Count) Then
ALimit := Count;
If (ALimit > MaxCollectionSize) Then
ALimit := MaxCollectionSize;
If (ALimit <> Limit) Then
Begin
SetLength(Items, ALimit);
Limit := ALimit;
End;
END;
PROCEDURE TCollection.Error (Code, Info: Integer);
BEGIN
raise EFPCollectionError.Create('FP Collection error '+IntToStr(Code)+' '+
IntToStr(Info)
);
END;
PROCEDURE TCollection.AtPut (Index: Sw_Integer; Item: Pointer);
BEGIN
If (Index >= 0) AND (Index < Count) Then
Items[Index] := Item
Else Error(coIndexError, Index);
END;
PROCEDURE TCollection.AtInsert (Index: Sw_Integer; Item: Pointer);
VAR I: Sw_Integer;
BEGIN
If (Index >= 0) AND (Index <= Count) Then Begin
If (Count=Limit) Then SetLimit(Limit+Delta);
If (Limit>Count) Then Begin
If (Index < Count) Then Begin
Move(Items[Index], Items[Index+1],
(Count - Index) * sizeof(Items[0])
);
End;
Items[Index] := Item;
Inc(Count);
End Else Error(coOverflow, Index);
End Else Error(coIndexError, Index);
END;
CONSTRUCTOR TSortedCollection.Create (ALimit, ADelta: Sw_Integer);
BEGIN
Inherited Create(ALimit, ADelta);
Duplicates := False;
END;
FUNCTION TSortedCollection.KeyOf (Item: Pointer): Pointer;
BEGIN
KeyOf := Item;
END;
FUNCTION TSortedCollection.IndexOf (Item: Pointer): Sw_Integer;
VAR I, J: Sw_Integer;
BEGIN
J := -1;
If Search(KeyOf(Item), I) Then Begin
If Duplicates Then
While (I < Count) AND (Item <> Items[I]) Do
Inc(I);
If (I < Count) Then J := I;
End;
IndexOf := J;
END;
FUNCTION TSortedCollection.Search (Key: Pointer; Var Index: Sw_Integer): Boolean;
VAR L, H, I, C: Sw_Integer;
BEGIN
Search := False;
L := 0;
H := Count - 1;
While (L <= H) Do Begin
I := (L + H) SHR 1;
C := Compare(KeyOf(Items[I]), Key);
If (C < 0) Then L := I + 1 Else Begin
H := I - 1;
If C = 0 Then Begin
Search := True;
If NOT Duplicates Then L := I;
End;
End;
End;
Index := L;
END;
PROCEDURE TSortedCollection.Insert (Item: Pointer);
VAR I: Sw_Integer;
BEGIN
If NOT Search(KeyOf(Item), I) OR Duplicates Then
AtInsert(I, Item);
END;
end.
-
За код спасибо. Хотя я, пожалуй, попробую оформить tSortedCollection как наследника от TCollection из модуля classes - для экономии кода.
-
Вот, сваял демку на основе TList. Это именно демка, вместо реального чтения исходника она генерит элементы через random. Но принцип организации экспорта элементов она должна отражать полностью. Жду замечаний :^)
{$mode ObjFpc} uses Classes; const eo_index=1; procsym=0; type tsym=record typ:byte; end;
texported_item = class options:byte; index:longint; sym:tsym; name:string; end;
var Indexes:array of longint; procedure MakeSequence(var x:array of longint;base,len:longint); var i,max,y,z:longint; begin max:=pred(len); for i:=0 to max do x[i]:=i+base; for i:=0 to max do begin y:=random(succ(max)); z:=x[max]; x[max]:=x[y]; x[y]:=z; dec(max); end; end;
function IdxCompare(Item1, Item2: Pointer): Integer; var I1:texported_item absolute Item1; I2:texported_item absolute Item2; begin Result:=I1.index-I2.index; end;
var EList_indexed:tLIst; EList_nonindexed:tLIst; hp:texported_item; i:longint; autoindex:longint;
procedure MoveItem(i:longint); begin EList_indexed.Insert(i,EList_nonindexed.Items[0]); EList_nonindexed.Delete(0); texported_item(EList_indexed.Items[i]).index:=autoindex; end;
begin setlength(Indexes,40); randomize; MakeSequence(Indexes,1,40);
EList_indexed:=tList.Create; EList_nonindexed:=tList.Create;
for i:=1 to 14 do begin
hp:=texported_item.Create; hp.options:=random(2); hp.Index:=Indexes[i]; if hp.options and eo_index=eo_index then hp.Name:='i' else hp.Name:='n';
if hp.options and eo_index=eo_index then EList_indexed.Add(hp) else EList_nonindexed.Add(hp);
end;
writeln(ELIst_indexed.Count,' ',ELIst_nonindexed.Count); writeln;
ELIst_indexed.Sort(@IdxCompare); i:=0; autoindex:=1; while EList_nonindexed.Count>0 do begin if i=EList_indexed.Count then begin EList_indexed.Add(EList_nonindexed.Items[0]); EList_nonindexed.Delete(0); texported_item(EList_indexed.Items[pred(EList_indexed.Count)]).index:=autoindex; end else if texported_item(EList_indexed.Items[0]).index>1 then MoveItem(0) else if i>0 then begin autoindex:=succ(texported_item(EList_indexed.Items[pred(i)]).index); if texported_item(EList_indexed.Items[i]).index>autoindex then MoveItem(i); end; inc(i); end; writeln(ELIst_indexed.Count,' ',ELIst_nonindexed.Count); writeln;
for i:=0 to ELIst_indexed.Count-1 do writeln(texported_item(ELIst_indexed.Items[i]).index,' ',texported_item(ELIst_indexed.Items[i]).name); end.
-
Сегодня отправил патч разработчикам.
|