-
Приветствую всех обитателей делфимастера! Есть такой кодес, и есть вопрос как избавиться от 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
-
а почему именно от setlength, а не от move, например, или inc?
-
> а почему именно от 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; и стало совсем не понятно, поэтому хочу заменить более ясным кодом...
-
Тогда надо сначала отказаться от типа Sttring.
-
Германн ©
> Тогда надо сначала отказаться от типа Sttring.
как вы можете предложить решить вопрос с SetLength? Желательно винапишками
-
Подожди, ты уже чудеса какие-то показываешь. Я же сказал - выложи свой вариант кода, от которого можно оттолкнуться. Можно ссылкой на архив. В данном случае ты верно воспользовался моим советом, с использованием VirtualAlloc и похоже абсолютно прослушал вторую его часть, где я тебе говорил о динамических массивах...
Короче - без твоего варианта кода тут никак.
-
if NeedDataSize <= 0 then
begin
SetLength(IntermediateBuffer, 8192);
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, но что-то не отрабатывает...
-
> mobility © (26.04.13 23:50) [4] > > Германн © > > > Тогда надо сначала отказаться от типа Sttring. > > как вы можете предложить решить вопрос с SetLength? > Желательно винапишками >
Я уже сказал > сначала отказаться от типа Sttring
И заодно и от динмассивов. Тогда SetLength точно не понадобится. Но я не знаю твою задачу и не пониманию твои проблемы. Розыч вроде знает, Розыч поможет.
-
Да и VirtualAlloc я никогда не использовал. Старый добрый GetMem меня всегда устраивал полностью. :)
-
что такое IntermediateBuffer ?
-
имхо странное желание...
-
Автор пытается переделать мой пример: 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^
-
IntermediateBuffer и Buff теперь объявленны как Pointer От SetLength избавился, но теперь качается только первые ~819-~1091 байт (до этого все было норм) if NeedDataSize <= 0 then
begin
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
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;
-
Это лишнее: @IntermediateBuffer^ достаточно просто: IntermediateBuffer Проблема здесь:
Buff := VirtualAlloc(Nil, ContentLength + BytesRead, MEM_COMMIT or MEM_RESERVE, PAGE_EXECUTE_READWRITE);
Move(IntermediateBuffer, Buff, BytesRead); Во первых ты получаешь мемлик, теряя предыдущий Buff из-за повтоного выделения памяти. Во вторых здесь нужно реалоцировать память, а не перевыделять. В третьих нужно писать по правильному оффсету реалоцированной памяти.
-
> реалоцировать память, а не перевыделять.
Вот, блин, сказал :))) Привильный вариант: реалоцировать память, а не выделять ее заново
-
> Привильный вариант:
Кошмар, да что-ж такое-то? :)
-
Что-то я совсем запутался... как сделать чтобы буфер не перезаписывался и не было утечки? while InternetReadFile(UrlHandle, IntermediateBuffer,
1024, BytesRead) do
if BytesRead > 0 then
begin
VirtualAlloc(Buff, ContentLength + BytesRead, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Move(IntermediateBuffer, Buff, BytesRead);
Inc(ContentLength, BytesRead);
end
-
Тэксь, давай для начала определимся - а для чего тебе вообще это нужно? Ибо с реализацией у тебя кавардак, а чего ты в итоге хочешь добиться мне не понятно...
-
хочу переписать Ваш код чисто на виньапишках. задача хранить буфер скачиваемого файла только в переменной (без промежуточной записи в файл и ен используя MemoryStream's), для последующих операций над ней.
-
Хорошо, допустим. Тогда по пунктам: 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]
телепатор подсказывает мне, что непрерывное расширение буфера последовательными вызовами SetLength забивает память дельфового менеджера кучи пустыми блоками, и программа падает с Out of memory. Вариант решения — хранить принятые данные фрагментами в односвязном списке, например.
-
> RWolf © (27.04.13 22:15) [21]
Хм, очень интересная подача :) Но как? При расширении буфера выйдем на реаллок, фрагментация то откуда? :)
-
> 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 SetLength(s, 100000000 + 4*i);
SetLength(a, Length(a)+1);
a[High(a)]:=IntToStr(i);
end;
end;
-
> При расширении буфера выйдем на реаллок, фрагментация то > откуда?
А есть ли в задаче автора возможность > Но выделить нужно сразу столько, сколько потребуется ? Иначе использование флага HEAP_REALLOC_IN_PLACE_ONLY просто вызовет ошибку. А тогда фрагментации избежать не удастся ровно также как и при использовании SetLength.
P.S. Меня больше смущает ответ автора:
> с этим всё понятно, а вот полез в > procedure _LStrSetLength{ var str: AnsiString; newLength: > Integer}; >
|