【问题标题】:What is the best way to convert Bytes to Cardinal and vice versa将字节转换为基数的最佳方法是什么,反之亦然
【发布时间】:2009-11-09 09:01:43
【问题描述】:

我现在有以下代码。

type
  TByte4 = array[0..3] of Byte; // 32-bit

function CardinalToBytes(const Data: Cardinal): TByte4;
begin
  Result[0] := (Data shr 24) and 255;
  Result[1] := (Data shr 16) and 255;
  Result[2] := (Data shr 8) and 255;
  Result[3] := Data and 255;
end;

function BytesToCardinal(const Data: TByte4): Cardinal;
begin
  Result := (Data[0] * 16777216) +
            (Data[1] * 65536) +
            (Data[2] * 256) +
             Data[3];
end;

我想知道这是否是最快和最有效的方法(我确定不是:))。问题是这必须在 Delphi 2006 下的紧凑框架中工作(甚至不要问)。 我在一个 TEA 加密算法中使用它,该算法与 Delphi 的 Ansi 和 Unicode 版本以及 Delphi 2006 中的 .NET 和 .NET 紧凑框架一起使用。

因此,不允许使用“移动”和使用指针的类似功能(不,我不想要不安全的代码)。

编辑:

我还没有找到更好的方法来做到这一点。给出了一些很好的建议,但它们在 .NET 中都失败了。恐怕我不会再花时间在死路上了。 VCL .NET 已死,恐怕德尔福中的 CF 也已死。所以这对于维护这个项目是必须的。如果有人证明我错了并让代码在 .NET 中编译,我会等待一段时间

编辑2:

解决方案很简单,因为它在大多数情况下都是如此。有人只需要寻找明显的解决方案。我只是不再知道 .NET 中有一个 BitConverter 类

【问题讨论】:

  • 我会使用 shl 而不是乘法来使代码更清晰。
  • 好主意,它也会更快。你为什么不把它作为答案。现在你的评论是最好的解决方案,或者我应该说改进。
  • 如果编译器不知道乘以 2 的幂与移位相同并且只需为您进行转换,则移位只会比乘法快。

标签: .net delphi byte


【解决方案1】:

在 Delphi.NET 中,使用 .NET 框架中的 BitConverter 类进行转换。见BitConverter Class (System)

举个例子:

function CardinalToBytes(const Data: Cardinal): TBytes;
begin
  Result := BitConverter.ToBytes(Data);
end;

function BytesToCardinal(const Data: TBytes): Cardinal;
begin
  Result := BitConverter.ToUInt32(Data, 0);
end;

【讨论】:

  • 有没有办法告诉类以大端顺序返回字节?文档示例以 little-endian 顺序显示结果,但这不是原始函数的作用。
  • 这行得通。开箱即用的好想法。我忘记了BitConverter。我做了一些 .NET 编程很长一段时间。这就是我需要的解决方案。简单的条件定义和每个平台一行。第一个函数应该是:Result := BitConverter.GetBytes(Data);
  • @Rob Hm 我试过了,它有效 :) 我稍后会深入研究,现在还有工作要做 :) 但感谢您指出这一点。
  • 这可能无关紧要,因为我进行加密和解密,并且在这两种情况下我都使用相同的字节序。如果其他一些 TEA 算法试图读取我的数据,就会出现问题。
  • BitConverter 不支持大端转换。不过,您可以使用 .NET 函数 IPAddress.NetworkToHostOrder 在字节序之间进行转换。
【解决方案2】:

好的,这有点快...

别忘了关闭范围检查!

function CardinalToBytes(const Data: Cardinal): TByte4;
begin
  Result[0] := Data shr 24;
  Result[1] := Data shr 16;
  Result[2] := Data shr 8;
  Result[3] := Data;
end;

【讨论】:

  • 嗯,我不确定是否值得权衡。我指的是关闭范围检查(仅针对部分代码的事件)。但你确实让它更快,所以如果没有更好的解决方案,我会接受你的回答。
  • 为什么不值得权衡?您是否担心您的一个字节可能超出一个字节的范围?这永远不会发生,所以没有什么可以检查范围的。编译器不需要知道这一点,因此除非您告诉它不要这样做,否则它可能会在那里保留范围检查代码。
  • 好的,没错,但是提出了正确的解决方案。尽管如此,如果我坚持我的旧解决方案,这将是一个可行的改进。
【解决方案3】:

一种方法是使用案例记录:

type
  TTestRecord = record
    case Cardinal of
      0: (Card: Cardinal);
      1: (Arr: array[0..3] of Byte);
  end;

var
  TR: TTestRecord;
begin

  TR.Arr[0] := 0;
  TR.Arr[1] := 1;
  TR.Arr[2] := 2;
  TR.Arr[3] := 3;
  TR.Card := 0;

【讨论】:

  • 这是一个很好的解决方案。需要“100000000”次迭代才能将基数分配给记录,将其读取到字节并将其分配给另一条记录,平均为 436 毫秒。对于相同的任务,我的代码执行相同数量的迭代需要 2387 毫秒。但是该死的东西仍然无法在 .NET 中编译,因为不允许使用动态结构并且编译器会阻塞 :( 但是感谢您的提示,这是一个很棒且干净的提示。
【解决方案4】:

分别

结果:=TByte4(Data);

和 结果:=基数(数据)

不过,这与 endianwise 不同(您或多或少地实现了一个 host to little endian 函数并返回到上面)。

另外,还有联合技巧:

  type 
   TSomeRecord = packed record
                case boolean of 
                    true : (data4:TByte4);
                    false :(dateabyte:cardinal);
                 end;

但我认为 .NET 也会对此感到厌烦。

【讨论】:

  • 这在 Delphi 2006 .NET 中不起作用。这是这里最难的部分。它最初是按照您显示的方式编码的,但我必须对其进行更改以使其与 .NET 兼容。结果:= TByte4(数据);抛出“无效的类型转换”。那样会很容易;)
  • 经典的联合技巧呢?它适用于旧的字节码语言(UCSD Pascal)
  • 好主意,但看看 Remko 发布的答案 :) 恐怕 .NET 在这里很烂 :) 现在我知道我为什么喜欢原生代码了。
  • 也许通过编组可以完成某些事情或使用不安全的代码。但是 2006 年的 CF 仍然是 1.0 版本,相信我它会让你发疯。感觉就像 10 年前的编程。
【解决方案5】:

你没有说没有助记词,所以……

type
  TByte4 = packed array[0..3] of Byte; // 32-bit

function CardinalToBytes(const Data: Cardinal): TByte4;
asm
  bswap  eax
end;

function BytesToCardinal(const Data: TByte4): Cardinal;
asm
  bswap  eax
end;

【讨论】:

  • 没错,我没有说“没有助记符”,但我确实说 .NET 兼容。 .NET 中不允许使用 ASM。编译器抛出:不支持的语言功能:ASM :) 一定有其他更快的方法,还是我真的找到了最佳解决方案?
  • 这就是'bswap'助记符的目的,所以你的代码或多或少是最优的
  • 那我应该对自己更有信心:))
猜你喜欢
  • 1970-01-01
  • 2010-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-23
  • 2015-02-16
  • 1970-01-01
  • 2013-11-11
相关资源
最近更新 更多