我能否将缓冲区+大小转换为TBytes?

5

给定一个缓冲区和它的字节数大小,有没有办法将它转换成TBytes而不需要复制它?

例子:

procedure HandleBuffer(_Buffer: PByte; _BufSize: integer);
var
  Arr: TBytes;
  i: Integer;
begin
  // some clever code here to get contents of the buffer into the Array
  for i := 0 to Length(Arr)-1 do begin
    HandleByte(Arr[i]);
  end;
end;

当然,我可以复制这些数据:

procedure HandleBuffer(_Buffer: PByte; _BufSize: integer);
var
  Arr: TBytes;
  i: Integer;
begin
  // this works but is very inefficient
  SetLength(Arr, _BufSize);
  Move(PByte(_Buffer)^, Arr[0], _BufSize);
  //
  for i := 0 to Length(Arr)-1 do begin
    HandleByte(Arr[i]);
  end;
end;

但对于一个大Buffer(约一百兆字节),这意味着我需要双倍内存,而且还要花费大量时间不必要地复制数据。

我知道我可以简单地使用PByte来处理缓冲区中的每个字节,但我只想使用TBytes的解决方案。

我认为这是不可能的,但我以前也说错过。

1个回答

5

不,这是不可能的(除非使用不合理的黑客手段)。

问题在于TBytes = TArray<Byte> = array of Byte动态数组,非空动态数组的堆对象包含数组的引用计数和长度头信息

当给定一个字节数组的普通指针时,接受TBytes参数的函数可能(理所当然地)尝试读取(不存在的)头信息,那么你就会遇到严重的问题。

此外,动态数组是托管类型(如我提到的引用计数所示),因此您可能也会遇到问题。


然而,在您特定的示例代码中,您实际上并没有使用数据的动态数组特性,因此可以直接使用缓冲区进行操作:
procedure HandleBuffer(_Buffer: PByte; _BufSize: integer);
var
  i: Integer;
begin
  for i := 0 to _BufSize - 1 do
    HandleByte(_Buffer[i]);
end;

我也可以直接递增_Buffer而不使用它作为数组。不幸的是,这样做无法检测到任何索引错误(ERangeCheck),这也是我更喜欢使用TBytes的主要原因。另一方面,我只会在调试代码中打开范围检查。 - dummzeuch
如果您对条件范围检查感兴趣,那么请将PByte和Size封装到一个记录中,并为其提供一个property Items[index: Integer]: Byte,其中包含内联Getter,您可以在其中进行范围检查。 - Stefan Glienke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接