Я использую Delphi 2009 со строками Unicode.
Я пытаюсь закодировать очень большой файл, чтобы преобразовать его в Unicode:
var
Buffer: TBytes;
Value: string;
Value := Encoding.GetString(Buffer);
Это прекрасно работает для буфера размером 40 МБ, который удваивается в размере и возвращает значение в виде строки Unicode размером 80 МБ.
Когда я пытаюсь сделать это с буфером 300 МБ, это дает мне исключение EOutOfMemory.
Ну, это было не совсем неожиданно. Но я все равно решил проследить это.
Он входит в процедуру DynArraySetLength в системном блоке. В этой процедуре он переходит в кучу и вызывает ReallocMem. К моему удивлению, он успешно выделяет 665 124 864 байта !!!
Но затем в конце DynArraySetLength он вызывает FillChar:
// Set the new memory to all zero bits
FillChar((PAnsiChar(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);
Вы можете увидеть по комментарию, что это должно делать. В этой подпрограмме немногое, но именно она вызывает исключение EOutOfMemory. Вот FillChar из системного блока:
procedure _FillChar(var Dest; count: Integer; Value: Char);
{$IFDEF PUREPASCAL}
var
I: Integer;
P: PAnsiChar;
begin
P := PAnsiChar(@Dest);
for I := count-1 downto 0 do
P[I] := Value;
end;
{$ELSE}
asm // Size = 153 Bytes
CMP EDX, 32
MOV CH, CL // Copy Value into both Bytes of CX
JL @@Small
MOV [EAX ], CX // Fill First 8 Bytes
MOV [EAX+2], CX
MOV [EAX+4], CX
MOV [EAX+6], CX
SUB EDX, 16
FLD QWORD PTR [EAX]
FST QWORD PTR [EAX+EDX] // Fill Last 16 Bytes
FST QWORD PTR [EAX+EDX+8]
MOV ECX, EAX
AND ECX, 7 // 8-Byte Align Writes
SUB ECX, 8
SUB EAX, ECX
ADD EDX, ECX
ADD EAX, EDX
NEG EDX
@@Loop:
FST QWORD PTR [EAX+EDX] // Fill 16 Bytes per Loop
FST QWORD PTR [EAX+EDX+8]
ADD EDX, 16
JL @@Loop
FFREE ST(0)
FINCSTP
RET
NOP
NOP
NOP
@@Small:
TEST EDX, EDX
JLE @@Done
MOV [EAX+EDX-1], CL // Fill Last Byte
AND EDX, -2 // No. of Words to Fill
NEG EDX
LEA EDX, [@@SmallFill + 60 + EDX * 2]
JMP EDX
NOP // Align Jump Destinations
NOP
@@SmallFill:
MOV [EAX+28], CX
MOV [EAX+26], CX
MOV [EAX+24], CX
MOV [EAX+22], CX
MOV [EAX+20], CX
MOV [EAX+18], CX
MOV [EAX+16], CX
MOV [EAX+14], CX
MOV [EAX+12], CX
MOV [EAX+10], CX
MOV [EAX+ 8], CX
MOV [EAX+ 6], CX
MOV [EAX+ 4], CX
MOV [EAX+ 2], CX
MOV [EAX ], CX
RET // DO NOT REMOVE - This is for Alignment
@@Done:
end;
{$ENDIF}
Итак, моя память была выделена, но она вылетала, пытаясь заполнить ее нулями. Это не имеет смысла для меня. Насколько я понимаю, память даже не должна быть заполнена нулями - и это, вероятно, в любом случае потеря времени - поскольку оператор Encoding собирается заполнить его в любом случае.
Могу ли я как-то помешать Delphi заполнить память?
Или есть какой-то другой способ, которым я могу заставить Delphi успешно распределить эту память для меня?
Моя настоящая цель - сделать это заявление о кодировке для моего очень большого файла, поэтому любое решение, которое позволит это сделать, будет высоко оценено.
Вывод: см. Мои комментарии к ответам.
Это предупреждение, чтобы быть осторожным при отладке ассемблерного кода. Убедитесь, что вы разбили все строки «RET», так как я пропустил одну в середине процедуры FillChar и ошибочно пришел к выводу, что FillChar вызвал проблему. Спасибо, Мейсон, за то, что указал на это.
Мне придется разбить входные данные на чанки для обработки очень большого файла.