Вопрос про использование массивов

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Вопрос про использование массивов

Сообщение Сквозняк » 19.08.2012 12:14:14

Возник вопрос с автором кода. В коде есть тип массивов размером 64Кб в которые через хитрую систему указателей загружается информация, объём которой часто >64Кб и может достигать 64Мб. В моём коде используются исходники на трёх диалектах паскаля, в том числе и этот шедевр программисткой мысли. Раньше программа выпадала в ошибку сегментации строго во время закрытия и всё было нормально. После значительного апгрейда не моих исходников со странным использованием масссивов, стал дохнуть поток их использующий. Ошибка случается прямо во время использования такого массива, его индекс в это время >3000000. По моему мнению, для корректной работы у всех использующих данную библиотеку, значение индекса не должно вылезать за диапазон 0..65535, но автор утверждает что я ничего не понимаю и ошибка находится в моём коде а не в его. Тут уже вопрос принципа, у кого ошибка?

В общем, задолбала вся эта история - да здравствуют форки!
Сквозняк
энтузиаст
 
Сообщения: 1129
Зарегистрирован: 29.06.2006 22:08:32

Re: Вопрос про использование массивов

Сообщение vada » 20.08.2012 10:02:32

В коде есть тип массивов размером 64Кб в которые через хитрую систему указателей загружается информация

Не распарсил.
Какой тип массива? Какая информация? И как в 64К помещается 64М? Даже если очень хитрая система указателей это как-то никак.
Аватара пользователя
vada
энтузиаст
 
Сообщения: 691
Зарегистрирован: 14.02.2006 13:43:17

Re: Вопрос про использование массивов

Сообщение Putnick » 20.08.2012 14:11:10

Конечно, судить, не видя исходников, сложно, но... Если Вы обращаетесь к 300000 элементу массива, в котором не должно быть более 64К элементов, то, видимо, Вы в чём-то не правы.
Putnick
новенький
 
Сообщения: 62
Зарегистрирован: 18.03.2009 13:02:56

Re: Вопрос про использование массивов

Сообщение Сквозняк » 20.08.2012 17:06:38

vada писал(а):Какой тип массива? Какая информация? И как в 64К помещается 64М? Даже если очень хитрая система указателей это как-то никак.

Ещё как, оказывается средствами дельфийского паскаля можно симулировать кусочек ассемблера. Лишняя информация хранится где-то в памяти, с этим всё ясно, но зачем для этого насиловать массив постоянной длины, загадка. С почти таким же успехом можно поместить всю информацию одним куском в массив переменной длины и потом делать с ней что угодно.

Вот он тип, размером с сегмент досовской памяти:
Код: Выделить всё
type
  Ptr = {$IFDEF CPU64} QWORD {$ELSE} LongWord {$ENDIF};

  PByteArray     = ^TByteArray;
  TByteArray     = array[ 0..65535 ] of Byte;

Типов массивов очень мало, зачем их много при таком использовании.
Пример проблемного его применения:
Код: Выделить всё
function png_ReadIDAT( var pngMem : zglTMemory; pngHeader : zglTPNGHeader; var Data : PByteArray; Size : Integer ) : Boolean;
  var
    i            : Cardinal;
    CopyP        : procedure( Src, Dest : PByte; Width : Integer );
    pngIDATEnd   : LongWord;
    pngZStream   : z_stream_s;
    pngRowUsed   : Boolean;
    pngRowBuffer : array[ Boolean ] of PByteArray;
    pngRowSize   : LongWord;
    pngOffset    : LongWord;
begin
  Result     := TRUE;
  CopyP      := nil;
  pngRowUsed := TRUE;
  pngIDATEnd := pngMem.Position + Size;
  png_GetPixelInfo( pngHeader, pngRowSize, pngOffset );

  zlib_Init( pngZStream );

  zgl_GetMem( Pointer( pngRowBuffer[ FALSE ] ), pngRowSize + 1 );
  zgl_GetMem( Pointer( pngRowBuffer[ TRUE ] ), pngRowSize + 1 );

  case pngHeader.ColorType of
    PNG_COLOR_RGB:
      if pngHeader.BitDepth = 8 Then CopyP := png_CopyNonInterlacedRGB;
    PNG_COLOR_PALETTE, PNG_COLOR_GRAYSCALE:
      if ( pngHeader.BitDepth = 1 ) or ( pngHeader.BitDepth = 4 ) or ( pngHeader.BitDepth = 8 ) Then CopyP := png_CopyNonInterlacedPalette;
    PNG_COLOR_RGBALPHA:
      if pngHeader.BitDepth = 8 Then CopyP := png_CopyNonInterlacedRGBAlpha;
    PNG_COLOR_GRAYSCALEALPHA:
      if pngHeader.BitDepth = 8 Then CopyP := png_CopyNonInterlacedGrayscaleAlpha;
  else
    log_Add( 'PNG - Unsupported ColorType' );
    Result := FALSE;
  end;

  if Result Then
    for i := pngHeader.Height downto 1 do
      begin
        if png_DecodeIDAT( pngMem, pngZStream, pngIDATEnd, @pngRowBuffer[ pngRowUsed ][ 0 ], pngRowsize + 1 ) < 0 Then
          begin
            log_Add( 'PNG - Failed to decode IDAT chunk' );
            Result := FALSE;
            break;
          end;

        png_FilterRow( pngRowBuffer[ pngRowUsed ], pngRowBuffer[ not pngRowUsed ], pngRowSize, pngOffset );

        CopyP( @pngRowBuffer[ pngRowUsed ][ 1 ], @Data[ pngHeader.Width * 4 * ( i - 1 ) ], pngHeader.Width );

        pngRowUsed := not pngRowUsed;
      end;

  FreeMem( pngRowBuffer[ FALSE ] );
  FreeMem( pngRowBuffer[ TRUE ] );

  zlib_Free( pngZStream );
end;

Переменная var Data : PByteArray; в определённых случаях подглючивает в больших программах, эту либу приходится собирать с {$R-}. Размер индекса переменной равен ширине картинки умноженной на 4, умноженной на (высота - 1). Потом индекс уменьшается на 4096 и так пока вся картинка не загрузится в память.
Сквозняк
энтузиаст
 
Сообщения: 1129
Зарегистрирован: 29.06.2006 22:08:32

Re: Вопрос про использование массивов

Сообщение alexey38 » 20.08.2012 20:12:37

Все эти извращения с адресацией через индексы массива пошли от сишников, пересевших на паскаль. С этим нужно строго. Кто хочет писать в стиле С++, тот пусть пишет на С++. А если сел за паскаль, то используй приемущсетсва паскаля, и не делай из него извращения.
Нужно брать за правило все компилировать только с {$R+}, а {$R-} применять сугубо в случае, когда не хватает быстродействия, но уже на отлаженной программе.

Если ктому надо хитрое вычисление индекса, то есть переменная типа Pointer, которая приводится к LongInt или QWord, пиши функцию для вычисления смещения. Можно даже к контролем размерности сделать.
А из Pointer можно и через PByte адресоваться. Короче есть куча способов, как безопасно писать, и чтобы строгость языка помогала этому.
alexey38
долгожитель
 
Сообщения: 1627
Зарегистрирован: 27.04.2011 19:42:31

Re: Вопрос про использование массивов

Сообщение Sergei I. Gorelkin » 21.08.2012 09:03:18

Такое объявление массива пришло из ранних версий Дельфи, в которых можно использовать индексацию с указателями только в случае если указываемый тип является массивом (исключение - PChar и PWideChar). Верхний предел такого массива не имеет значения, кроме как порог срабатывания проверки диапазонов.
Чтобы этот код работал с {$R+}, нужно 65535 заменить на что-то вроде high(longint), или отключать проверку ( {$R-} ) вокруг использования указателей на эти массивы.

FPC позволяет индексировать любые указатели в стиле C, при этом проверка диапазонов не используется.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Вопрос про использование массивов

Сообщение vada » 21.08.2012 09:45:20

FPC позволяет индексировать любые указатели в стиле C, при этом проверка диапазонов не используется.

Я полностью согласен с alexey38. За такие извраты над паскалем надо пальцы программеру ломать. :mrgreen:
Код: Выделить всё
CopyP( @pngRowBuffer[ pngRowUsed ][ 1 ], @Data[ pngHeader.Width * 4 * ( i - 1 ) ], pngHeader.Width );

Смело! Очень смело! :(
Аватара пользователя
vada
энтузиаст
 
Сообщения: 691
Зарегистрирован: 14.02.2006 13:43:17

Re: Вопрос про использование массивов

Сообщение hinst » 21.08.2012 13:55:19

Мне лично всё равно, как разработчики реализуют свои библиотеки. По крайней мере, пока они стабильно работают и мне не приходится самому в них копаться чтобы выяснить, почему они не работают.
Код: Выделить всё
TByteArray = array[ 0..65535 ] of Byte;

Интересно, а почему не 66666 ? что, так не заработало бы? почему не [0..1]? если тип всё равно используется не по назначению, какой вообще смысл задавать эту длину? TByteArray наверняка нигде на прямую не используется, только через PByteArray. Так что, можно было просто написать
Код: Выделить всё
PByteArray = ^byte
и всё работало бы точно так же.

ZenGL у меня компилируется с глобально включённой проверкой диапазона массива и текстуры из PNG грузятся. Не знаю, правда, может быть, эта самая проверка диапазона там где-нибудь локально в отдельных исходниках отключается, и глобальный переключатель перекрывается этим. Однако при малейших попытках работать с функциями движка из разных потоков всё падает намертво. Сдаётся мне, он для этого не предназначен. ThreadUnsafe. Да и вообще, интересно, может ли видеокарта работать многопоточно? грузить сразу две текстуры в память? и будут ли они от этого грузиться быстрее? лол. Ну я в видеокартах не эксперт. У меня GT 430
Аватара пользователя
hinst
энтузиаст
 
Сообщения: 781
Зарегистрирован: 12.04.2008 18:32:38

Re: Вопрос про использование массивов

Сообщение Сквозняк » 21.08.2012 20:15:00

Интересно, а почему не 66666 ? что, так не заработало бы?

Этот размер у меня вызывает стойкую ассоциацию что его использующий воссоздаёт досовскую память. В BP7 если написать процедуру чуть меньше сегмента памяти и после компиляции модуля записать в неё информацию, то после статической линковки с программой эта информация не прыгает по памяти, к ней можно иметь стабильный доступ, к сожалению, только на чтение.

ZenGL у меня компилируется с глобально включённой проверкой диапазона массива и текстуры из PNG грузятся.

На ревизии 1297 и у меня он работал с проверкой диапазона, но после обновления на 1889 это стало невозможно. При таком подходе добавление новых фич должно увеличивать количество глюков при нестандартном использовании движка.

Однако при малейших попытках работать с функциями движка из разных потоков всё падает намертво. Сдаётся мне, он для этого не предназначен.
В демках многопоточная загрузка работает, пиши код точно так как написан движок с демками, может будет работать и дальше :mrgreen:

и будут ли они от этого грузиться быстрее?

Можно грузить в один поток с параллельным выводом заставки на экран. В процедуре init первой грузится текстура с заставкой, выводится на экран, происходит прыжок к концу процедуры или exit, в draw опять прыжок к концу, потом в init опять прыжок и грузятся текстуры которые ещё не загружены. Если делать много прыжков, то можно и градусник с загрузкой нарисовать.
Сквозняк
энтузиаст
 
Сообщения: 1129
Зарегистрирован: 29.06.2006 22:08:32


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 3

Рейтинг@Mail.ru