perl: 构建 big/little endian bigfields 并用单个包输出它们

perl: build big/little endian bigfields and output them with a single pack

我正在用 perl 构建二进制数据。 此二进制数据基于 C 结构,用于 32 位和 64 位、大端和小端系统。 困难的部分是 FORMAT 结构中的位域。在 little/big endian 架构中,这在内存中的布局不同。

我目前正在制作这样的位域:

struct FORMAT
{
 void * X,
 void * Y,
 void * Z,
 unsigned int size   : 26;
 unsigned int type   :  6;
 <4 byte padding on 64bit targets>
}

my $size = 0x3C;
my $type = 0x05;

=> big endian
print pack("L>", $size << 6) | pack("L>", $type);  # 00 00 0f 05
=> little endian
print pack("L<", $size) | pack("L<", $type << 26); # 3c 00 00 14

但我希望将上面的内容以单个包的格式呈现,(因此不会调用 pack 两次,然后将结果或结果打印出来。)

最终我想一次打印一整条 FORMAT 记录。

 pack("Q>3L>L>", X, Y, Z, ???, 0) #64bit big endian

我有大约 50 万条 FORMAT 记录需要写出,并且为每条记录多次调用 pack() 成本太高。

你可以使用 XS。这将为您处理字节顺序和特定于编译器的对齐方式(填充)。

use strict;
use warnings;

use Inline C => <<'__EOC__';

   typedef struct {
      void* X;
      void* Y;
      void* Z;
      unsigned int size: 26;
      unsigned int type:  6;
   } FORMAT;

   SV* pack_FORMAT(UV X, UV Y, UV Z, unsigned int size, unsigned int type) {
      FORMAT format;
      format.X = INT2PTR(void*, X);
      format.Y = INT2PTR(void*, Y);
      format.Z = INT2PTR(void*, Z);
      format.size = size;
      format.type = type;
      return newSVpvn(&format, sizeof(format));
   }

__EOC__

my $size = 0x3C;
my $type = 0x05;

my $packed = pack_FORMAT(0, 0, 0, $size, $type);
printf("%v02X\n", $packed);

也就是说

pack("L>", $size << 6) | pack("L>", $type)
pack("L<", $size) | pack("L<", $type << 26)

可以写成

pack("L>", ($size << 6) | $type)
pack("L<", $size | ($type << 26))