在 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

TCustomMemoryStreamTMemoryStream 中的大型 (>2GB) 文件都存在问题。在 TMemoryStream 中,问题很简单,因为局部变量需要声明为 NativeInt 而不是 LongInt,并且 Capacity 需要更改为 NativeInt。在 TCustomMemoryStream 中,它们更加微妙,因为两种 TCustomMemoryStream.Read 方法都将 Int64 - Int64 计算的结果直接分配给 LongInt。即使此计算的结果不大于 LongInt.

,这也会溢出

如果你想在西雅图解决这个问题,那么你需要做一个代码挂钩,更换 System.Classes 单元,或者为 TMemoryStream 推出你自己的替代品 class。请记住,对于最后一个选项,您还需要替换 TBytesStreamTStringStream,因为它们源自 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;