Конференция "WinAPI" » Как избавиться от SetLength? [D7]
 
  • mobility © (26.04.13 22:33) [0]
    Приветствую всех обитателей делфимастера!
    Есть такой кодес, и есть вопрос как избавиться от SetLength в нем.

           if NeedDataSize <= 0 then
           begin
             SetLength(IntermediateBuffer, 8192);   // например тут
             ContentLength := 0;
             NeedDataSize := 0;
             BytesRead := 0;
             ....тут часть кодеса....
                 SetLength(Buff, ContentLength + BytesRead);
                 Move(IntermediateBuffer[0], Buff[ContentLength], BytesRead);
                 Inc(ContentLength, BytesRead);
               end
               else
               begin
                 NeedDataSize := ContentLength;
                 Break;
               end;
           end;

    пробовал так:
    IntermediateBuffer  := VirtualAlloc(Nil, 8192, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    но вылетает AV
    0040403F   .  F0:FF42 F8    LOCK INC DWORD PTR DS:[EDX-8]            ;  LOCK prefix
  • clickmaker © (26.04.13 23:23) [1]
    а почему именно от setlength, а не от move, например, или inc?
  • mobility © (26.04.13 23:29) [2]

    > а почему именно от setlength, а не от move, например, или
    > inc?

    с этим всё понятно, а вот полез в
    procedure       _LStrSetLength{ var str: AnsiString; newLength: Integer};
    type
     PStrRec = ^StrRec;
     StrRec = packed record
       refCnt: Longint;
       length: Longint;
     end;

    const
     skew = SizeOf(StrRec);
     rOff = SizeOf(StrRec); { refCnt offset }
     overHead = SizeOf(StrRec) + 1;
    asm
           { ->    EAX     Pointer to str  }
           {       EDX new length  }

           PUSH    EBX
           PUSH    ESI
           PUSH    EDI
           MOV     EBX,EAX
           MOV     ESI,EDX
           XOR     EDI,EDI

           TEST    EDX,EDX
           JLE     @@setString

           MOV     EAX,[EBX]
           TEST    EAX,EAX
           JE      @@copyString

           CMP     [EAX-skew].StrRec.refCnt,1
           JNE     @@copyString

           SUB     EAX,rOff
           ADD     EDX,rOff+1
           PUSH    EAX
           MOV     EAX,ESP
           CALL    _ReallocMem
           POP     EAX
           ADD     EAX,rOff
           MOV     [EBX],EAX
           MOV     [EAX-skew].StrRec.length,ESI
           MOV     BYTE PTR [EAX+ESI],0
           JMP     @@exit

    @@copyString:
           MOV     EAX,EDX
           CALL    _NewAnsiString
           MOV     EDI,EAX

           MOV     EAX,[EBX]
           TEST    EAX,EAX
           JE      @@setString

           MOV     EDX,EDI
           MOV     ECX,[EAX-skew].StrRec.length
           CMP     ECX,ESI
           JL      @@moveString
           MOV     ECX,ESI

    @@moveString:
           CALL    Move

    @@setString:
           MOV     EAX,EBX
           CALL    _LStrClr
           MOV     [EBX],EDI

    @@exit:
           POP     EDI
           POP     ESI
           POP     EBX
    end;
    и стало совсем не понятно, поэтому хочу заменить более ясным кодом...
  • Германн © (26.04.13 23:33) [3]
    Тогда надо сначала отказаться от типа Sttring.
  • mobility © (26.04.13 23:50) [4]
    Германн ©

    > Тогда надо сначала отказаться от типа Sttring.

    как вы можете предложить решить вопрос с SetLength?
    Желательно винапишками
  • Rouse_ © (27.04.13 00:33) [5]
    Подожди, ты уже чудеса какие-то показываешь.
    Я же сказал - выложи свой вариант кода, от которого можно оттолкнуться. Можно ссылкой на архив.
    В данном случае ты верно воспользовался моим советом, с использованием VirtualAlloc и похоже абсолютно прослушал вторую его часть, где я тебе говорил о динамических массивах...

    Короче - без твоего варианта кода тут никак.
  • mobility © (27.04.13 01:27) [6]
    if NeedDataSize <= 0 then
           begin
             SetLength(IntermediateBuffer, 8192);    
             //IntermediateBuffer     := VirtualAlloc(Nil, 8192, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
             ContentLength := 0;
             NeedDataSize := 0;
             BytesRead := 0;
             while InternetReadFile(UrlHandle, @IntermediateBuffer[0],
               1024, BytesRead) do
               if BytesRead > 0 then
               begin
                 SetLength(Buff, ContentLength + BytesRead);
                 Move(IntermediateBuffer[0], Buff[ContentLength], BytesRead);
                 Inc(ContentLength, BytesRead);
               end
               else
               begin
                 NeedDataSize := ContentLength;
                 Break;
               end;
           end;



    вот код. собсно в двух местах, где SetLength, я и хочу заменить на VirtualAlloc, но что-то не отрабатывает...
  • Германн © (27.04.13 02:14) [7]

    > mobility ©   (26.04.13 23:50) [4]
    >
    > Германн ©
    >
    > > Тогда надо сначала отказаться от типа Sttring.
    >
    > как вы можете предложить решить вопрос с SetLength?
    > Желательно винапишками
    >

    Я уже сказал
    > сначала отказаться от типа Sttring

    И заодно и от динмассивов. Тогда SetLength точно не понадобится. Но я не знаю твою задачу и не пониманию твои проблемы.
    Розыч вроде знает, Розыч поможет.
  • Германн © (27.04.13 02:22) [8]
    Да и VirtualAlloc я никогда не использовал. Старый добрый GetMem меня всегда устраивал полностью. :)
  • MBo © (27.04.13 06:37) [9]
    что такое IntermediateBuffer ?
  • brother © (27.04.13 06:57) [10]
    имхо странное желание...
  • Rouse_ © (27.04.13 15:08) [11]
    Автор пытается переделать мой пример: http://rouse.drkb.ru/network.php#proxyload


    > mobility ©   (27.04.13 01:27) [6]


    Смотри, ты выделяешь память под динмассив:

    IntermediateBuffer := VirtualAlloc(Nil, 8192, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);



    и работаешь с ним как с массивом:

    Move(IntermediateBuffer[0], Buff[ContentLength], BytesRead);



    А это не верно, IntermediateBuffer в данном случае должен быть объявлен как Pointer (хотя-бы) и работать с ним в данном случае нужно через разименование:

    IntermediateBuffer^

  • mobility © (27.04.13 17:10) [12]
    IntermediateBuffer и Buff теперь объявленны как Pointer
    От SetLength избавился, но теперь качается только первые ~819-~1091 байт (до этого все было норм)

    if NeedDataSize <= 0 then
           begin
             //SetLength(IntermediateBuffer, 8192);
             IntermediateBuffer     := VirtualAlloc(Nil, 8192, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
             ContentLength := 0;
             NeedDataSize := 0;
             BytesRead := 0;
             while InternetReadFile(UrlHandle, @IntermediateBuffer^,
               1024, BytesRead) do
               if BytesRead > 0 then
               begin
                 //SetLength(Buff, ContentLength + BytesRead);
                 Buff     := VirtualAlloc(Nil, ContentLength + BytesRead, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
                 Move(IntermediateBuffer, Buff, BytesRead);
                 Inc(ContentLength, BytesRead);
               end
               else
               begin
                 NeedDataSize := ContentLength;
                 Break;
               end;
           end;

  • Rouse_ © (27.04.13 17:28) [13]
    Это лишнее:

    @IntermediateBuffer^



    достаточно просто:

    IntermediateBuffer



    Проблема здесь:


    Buff := VirtualAlloc(Nil, ContentLength + BytesRead, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
    Move(IntermediateBuffer, Buff, BytesRead);



    Во первых ты получаешь мемлик, теряя предыдущий Buff из-за повтоного выделения памяти.
    Во вторых здесь нужно реалоцировать память, а не перевыделять.
    В третьих нужно писать по правильному оффсету реалоцированной памяти.
  • Rouse_ © (27.04.13 17:45) [14]

    > реалоцировать память, а не перевыделять.

    Вот, блин, сказал :)))
    Привильный вариант:
    реалоцировать память, а не выделять ее заново

  • Rouse_ © (27.04.13 17:46) [15]

    > Привильный вариант:

    Кошмар, да что-ж такое-то? :)
  • mobility © (27.04.13 19:51) [16]
    Что-то я совсем запутался... как сделать чтобы буфер не перезаписывался и не было утечки?
    while InternetReadFile(UrlHandle, IntermediateBuffer,
               1024, BytesRead) do
               if BytesRead > 0 then
               begin
                 //SetLength(Buff, ContentLength + BytesRead);
                 VirtualAlloc(Buff, ContentLength + BytesRead, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
                 //ReallocMem(Buff, ContentLength + BytesRead);
                 Move(IntermediateBuffer, Buff, BytesRead);
                 Inc(ContentLength, BytesRead);
               end

  • Rouse_ © (27.04.13 19:56) [17]
    Тэксь, давай для начала определимся - а для чего тебе вообще это нужно?
    Ибо с реализацией у тебя кавардак, а чего ты в итоге хочешь добиться мне не понятно...
  • mobility © (27.04.13 20:12) [18]
    хочу переписать Ваш код чисто на виньапишках.
    задача хранить буфер скачиваемого файла только в переменной (без промежуточной записи в файл и ен используя MemoryStream's), для последующих операций над ней.
  • Rouse_ © (27.04.13 20:24) [19]
    Хорошо, допустим.
    Тогда по пунктам:
    VirtualAlloc(Buff, ContentLength + BytesRead, MEM_COMMIT, PAGE_EXECUTE_READWRITE);



    Это выделение памяти, его нужно использовать только один раз при инициализации буфера приемника.

    Читай справку, по использемым тобой функциям:
    http://msdn.microsoft.com/en-us/library/windows/desktop/aa366887(v=vs.85).aspx

    Но выделить нужно сразу столько, сколько потребуется.

    Если ты планируешь расширять буфер памяти, можно использовать механизм кучи, который базируется на том-же VirtualAlloc

    А именно HeapAlloc
    http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597(v=vs.85).aspx

    и реаллокация через HeapReAlloc
    http://msdn.microsoft.com/en-us/library/windows/desktop/aa366704(v=vs.85).aspx

    далее, при работе с буфером учитывай тот момент, что у тебя есть только его база (адрес аллоцированной памяти) а для модификации тебе нужно использовать некий курсор - это переменная, посредством которой указывается точный оффсет от базы в который производится запись (или чтение).

    При записи в буфер частями, ты должен на каждой итерации цикла инкрементировать значения курсора на размер предыдущей части данных и производить запись в буфер, ориентируясь на значение курсоа.
  • DVM © (27.04.13 21:14) [20]

    > хочу переписать Ваш код чисто на виньапишках.

    А что это даст? Какие преимущества?
  • RWolf © (27.04.13 22:15) [21]

    > DVM ©   (27.04.13 21:14) [20]

    телепатор подсказывает мне, что непрерывное расширение буфера последовательными вызовами SetLength забивает память дельфового менеджера кучи пустыми блоками, и программа падает с Out of memory.
    Вариант решения — хранить принятые данные фрагментами в односвязном списке, например.
  • Rouse_ © (27.04.13 23:11) [22]

    > RWolf ©   (27.04.13 22:15) [21]

    Хм, очень интересная подача :)
    Но как?
    При расширении буфера выйдем на реаллок, фрагментация то откуда? :)
  • RWolf © (28.04.13 01:42) [23]

    > Rouse_ ©   (27.04.13 23:11) [22]


    я хочу сказать, что в [0] мы видим не весь код, так что, возможно, реализуется ситуация наподобие такой:

    procedure TForm1.Button1Click(Sender: TObject);
    var i:Integer;
       s:string;
       a:array of string;
    begin
     for i:=1 to 100 do begin //падаем на 20-й итерации
       SetLength(s, 100000000 + 4*i);
       SetLength(a, Length(a)+1);
       a[High(a)]:=IntToStr(i);
     end;
    end;

  • Германн © (28.04.13 02:11) [24]

    > При расширении буфера выйдем на реаллок, фрагментация то
    > откуда?

    А есть ли в задаче автора возможность
    > Но выделить нужно сразу столько, сколько потребуется
    ?
    Иначе использование флага HEAP_REALLOC_IN_PLACE_ONLY просто вызовет ошибку. А тогда фрагментации избежать не удастся ровно также как и при использовании SetLength.

    P.S.
    Меня  больше смущает ответ автора:

    > с этим всё понятно, а вот полез в
    > procedure       _LStrSetLength{ var str: AnsiString; newLength:
    >  Integer};
    >
 
Конференция "WinAPI" » Как избавиться от SetLength? [D7]
Есть новые Нет новых   [134428   +40][b:0][p:0.003]