在 Stream.read 中使用 Int64 大小的 Longint 计数不是很危险吗?
Isn't it dangerous to use the Longint count with the Int64 size in Stream.read?
我正在检查 TMemoryStream
class 并发现以下例程:
procedure TMemoryStream.LoadFromStream(Stream: TStream);
var
Count: Longint;
begin
Stream.Position := 0;
Count := Stream.Size; // <-- assigning Int64 to Longint
SetSize(Count);
if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);
end;
我经常看到这种模式,其中将 Int64 分配给 Longint。
我的理解是 Longint
is four bytes and Int64
在 32 位和 64 位中都是八个字节 Windows,所以如果我的文件大小是 FFFF FFFF == 8589934591 == 8 GB
那么这个例程将完全失败阅读,因为最终计数将是 $ FFFF FFFF == -1
。
我不明白这是怎么允许的,也许没有考虑到(可能没有多少人试图读取 8+ GB 的文件)。
我为此记录了一张票,它显然已在东京 10.2 中修复。这是 64 位编译的问题。
https://quality.embarcadero.com/browse/RSP-19094
TCustomMemoryStream
和 TMemoryStream
中的大型 (>2GB) 文件都存在问题。在 TMemoryStream
中,问题很简单,因为局部变量需要声明为 NativeInt
而不是 LongInt,并且 Capacity 需要更改为 NativeInt
。在 TCustomMemoryStream
中,它们更加微妙,因为两种 TCustomMemoryStream.Read
方法都将 Int64
- Int64
计算的结果直接分配给 LongInt
。即使此计算的结果不大于 LongInt
.
,这也会溢出
如果你想在西雅图解决这个问题,那么你需要做一个代码挂钩,更换 System.Classes 单元,或者为 TMemoryStream
推出你自己的替代品 class。请记住,对于最后一个选项,您还需要替换 TBytesStream
和 TStringStream
,因为它们源自 TMemoryStream
.
最后一个选项的另一个问题是第三方组件不会有您的 "fixes"。对于我们来说,我们只有几个地方需要处理大于 2GB 的文件,所以我们将它们切换了。
TCustomMemoryStream.Read 的修复(必须针对两种方法)看起来像这样:
function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
{ These 2 lines are new }
var
remaining: Int64;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
remaining{Result} := FSize - FPosition;
if remaining{Result} > 0 then
begin
if remaining{Result} > Count then
Result := Count
else
Result := remaining;
Move((PByte(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;
我正在检查 TMemoryStream
class 并发现以下例程:
procedure TMemoryStream.LoadFromStream(Stream: TStream);
var
Count: Longint;
begin
Stream.Position := 0;
Count := Stream.Size; // <-- assigning Int64 to Longint
SetSize(Count);
if Count <> 0 then Stream.ReadBuffer(FMemory^, Count);
end;
我经常看到这种模式,其中将 Int64 分配给 Longint。
我的理解是 Longint
is four bytes and Int64
在 32 位和 64 位中都是八个字节 Windows,所以如果我的文件大小是 FFFF FFFF == 8589934591 == 8 GB
那么这个例程将完全失败阅读,因为最终计数将是 $ FFFF FFFF == -1
。
我不明白这是怎么允许的,也许没有考虑到(可能没有多少人试图读取 8+ GB 的文件)。
我为此记录了一张票,它显然已在东京 10.2 中修复。这是 64 位编译的问题。
https://quality.embarcadero.com/browse/RSP-19094
TCustomMemoryStream
和 TMemoryStream
中的大型 (>2GB) 文件都存在问题。在 TMemoryStream
中,问题很简单,因为局部变量需要声明为 NativeInt
而不是 LongInt,并且 Capacity 需要更改为 NativeInt
。在 TCustomMemoryStream
中,它们更加微妙,因为两种 TCustomMemoryStream.Read
方法都将 Int64
- Int64
计算的结果直接分配给 LongInt
。即使此计算的结果不大于 LongInt
.
如果你想在西雅图解决这个问题,那么你需要做一个代码挂钩,更换 System.Classes 单元,或者为 TMemoryStream
推出你自己的替代品 class。请记住,对于最后一个选项,您还需要替换 TBytesStream
和 TStringStream
,因为它们源自 TMemoryStream
.
最后一个选项的另一个问题是第三方组件不会有您的 "fixes"。对于我们来说,我们只有几个地方需要处理大于 2GB 的文件,所以我们将它们切换了。
TCustomMemoryStream.Read 的修复(必须针对两种方法)看起来像这样:
function TCustomMemoryStream.Read(var Buffer; Count: Longint): Longint;
{ These 2 lines are new }
var
remaining: Int64;
begin
if (FPosition >= 0) and (Count >= 0) then
begin
remaining{Result} := FSize - FPosition;
if remaining{Result} > 0 then
begin
if remaining{Result} > Count then
Result := Count
else
Result := remaining;
Move((PByte(FMemory) + FPosition)^, Buffer, Result);
Inc(FPosition, Result);
Exit;
end;
end;
Result := 0;
end;