Конференция "FreePascal" » Требуется багфикс...
 
  • PVOzerski © (12.04.04 17:06) [0]
    Хочу поставить маленький эксперимент по привлечению обитателей "Мастаков" в ряды сообщества OpenSource :^). Итак, имеется баг в компиляторе, нужен хороший багфикс (мой оказался дефективным).
    Суть проблемы.
    Как известно, на некоторых платформах, включая Win32, экспорт и импорт процедур и функций возможен не только по именам, но и по целочисленным индексам. До недавнего времени при компиляции раздела Exports компилятор FreePascal, прочитав очередное имя экспортируемого элемента, немедленно генерил платформо-зависимый код. Если элемент имел индекс, который был больше предшествующего, индекс экспортировался и запоминался, как пердшествующий для следующего. Такая схема нормально работала, только если индексы следовали в секции Exports в возрастающем порядке. Если порядок нарушался или индексы имелись не у всех элементов, начинались ложные срабатывания на повторный индекс. Я предложил сначала делать связанный список экспортируемых элементов, сортировать его и лишь потом экспортировать. Однако путь этот не очень хорош, поскольку список может отъедать много памяти, а реально индексы используются не так уж часто. Если есть идеи, готов пообсуждать.
  • pasha_golub © (13.04.04 12:16) [1]
    Хочу помочь, но чего-то формулировки не понял. :-)
  • PVOzerski © (13.04.04 12:44) [2]
    2pasha_golub:
    До последнего багфикса при компиляции подобного:
    exports
    proc1 index 2,
    proc2 index 1;
    компилятор выдавал сообщение об ошибке. Сейчас эта проблема решена, однако вот такое скомпилировать по-прежнему невозможно:
    exports
    proc1,
    proc2 index 1;
    Хотелось бы придумать разумное решение проблемы. Подробности об исходном тексте дам позднее (сейчас я нахожусь не там, где у меня он есть :^)).
  • pasha_golub © (13.04.04 13:50) [3]
    proc1 должно иметь индекс или нет?
  • PVOzerski © (13.04.04 15:07) [4]
    Сейчас если все экспортируемые модули имеют индексы (или все не имеют), всё ОК. А вот если смесь индексированных и не индексированных (как во 2-м примере), возникает трабл. Ординал в PECOFF-файле все равно нужен, просто либо компилятор генерит его автоматически, либо испрользует значение индекса.
  • pasha_golub © (13.04.04 15:14) [5]
    А у меня тут вота вопрос такой ламерский. Компилятор Паскаль он типа однопроходный, это то есть за одно прочтение текста модуля он его построить должон? Если за несколько проходов, то можно вроде.
  • Verg © (13.04.04 18:40) [6]

    > Я предложил сначала делать связанный список экспортируемых
    > элементов, сортировать его и лишь потом экспортировать.
    > Однако путь этот не очень хорош, поскольку список может
    > отъедать много памяти, а реально индексы используются не
    > так уж часто.


    Нет, мне это непонятно... Там где индексы используются редко, там и список не отъест много памяти, а там где часто - там столько, сколько надо, столько и отъест. На то он и список.
    И в текщей реализации же где-то запоминается - какой индекс уже использован, а какой нет.
    Ну не хочется списком, можно сортированным дин. массивом, будет меньше кушать памяти, зато тормознутее будет. Кстати, если учесть, что этот массив в принципе только увеличивается в размере, то и не так уж тормознуто будет.
    И вообще, что значит "много"?
  • PVOzerski © (13.04.04 18:59) [7]
    >И в текщей реализации же где-то запоминается - какой индекс уже использован, а какой нет.
    В предшествующих версиях шло лишь сравнение, больше ли обрабатываемый индекс предыдущего.

    >Ну не хочется списком, можно сортированным дин. массивом, будет
    >меньше кушать памяти, зато тормознутее будет. Кстати, если учесть,
    > что этот массив в принципе только увеличивается в размере, то и >не так уж тормознуто будет.

    А есть ли уверенность, что изменение объема выделенного блока памяти в динамическом массиве не сопровождается скрытым временным выделением нового блока с последующим копированием и освобождением старого? И не будет ли расход памяти из-за этого только хуже?
  • pasha_golub © (13.04.04 19:21) [8]
    А можно дин. массив, а чтобы кушало медленнее работать побитово.
  • Jack128 © (13.04.04 20:27) [9]
    может я совсем дуб, но не вижу проблем..
    Запоминает (не обязательно в дин массиве) индексы, как встретили экспортируемую функцию с индексом - ищем этот индекс - нашли -> ошибка | не нашли -> добавляем индекс массив.. Варианты реализации будут зависить от того на какое кол-во экспорт. функций оптимизировать..

    > А есть ли уверенность, что изменение объема выделенного
    > блока памяти в динамическом массиве не сопровождается скрытым
    > временным выделением нового блока с последующим копированием
    > и освобождением старого?
    А возможны другие варианты? Просто разом длину массива побольше выставить. Я так понял - на это Verg намекает..
  • Verg © (14.04.04 00:24) [10]

    > А есть ли уверенность, что изменение объема выделенного
    > блока памяти в динамическом массиве не сопровождается скрытым
    > временным выделением нового блока с последующим копированием
    > и освобождением старого? И не будет ли расход памяти из-за
    > этого только хуже?


    Абсолютно уверен (знаю), что сопровождается "скрытым временным" ипроч. Но почему это так смущает? Ну не надо приращать размер выделенной памяти под массив (reallocmem) на каждую добавленную к нему запись, добавляйте с неким Delta (5-10-20).

    Расход памяти будет только меньше и "потряхиваний кучи" тоже, т.к. связный список хорош тогда, когда идет постояннное и пополнение и удаление из него, а массив хорош - во всех остальных случаях :)

    С сортированным (сортирующимся) массивом будет больше move-есов (перемещений его кусманов)
  • Verg © (14.04.04 09:04) [11]
    Пока мне кажется, что TSortedCollection (Objects.pas) - оптимальная реализация. Памяти чуть больше, чем на просто дин. массив, но зато намного меньшие куски памяти перемещаются при вставках.
  • PVOzerski © (14.04.04 09:58) [12]
    2Verg[11]: Принял к сведению, посмотрю.
  • PVOzerski © (14.04.04 17:49) [13]
    Кажется, не судьба использовать TSortedCollection из Objects.pas... Потому что нынешние исходники FreePascal, ориентированные на компиляцию самим компилятором FreePascal, "вычищены" от объектов синтаксиса BP и переведены на классы в стиле Delphi. Думаю, возврат к objects будет воспринят в штыки.
    Еще вопрос: а как быть, если target в принципе не поддерживает индексы. Компилятор-то тот же самый. Что дешевле: ввести в описание каждой платформы поле IndexSupport или маятся со списками/массвами и там, где можно обойтись одним проходом?
  • Verg © (14.04.04 21:01) [14]

    > Думаю, возврат к 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.

  • PVOzerski © (15.04.04 10:32) [15]
    За код спасибо. Хотя я, пожалуй, попробую оформить tSortedCollection как наследника от TCollection из модуля classes - для экономии кода.
  • PVOzerski © (15.04.04 13:52) [16]
    Вот, сваял демку на основе 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.
  • PVOzerski © (22.04.04 16:02) [17]
    Сегодня отправил патч разработчикам.
 
Конференция "FreePascal" » Требуется багфикс...
Есть новые Нет новых   [134430   +2][b:0][p:0.005]