`bitpacked`记录小端机问题
`bitpacked` records on the little-endian machine issue
我正在尝试在 小端计算机 上使用 FreePascal 来读取和解释来自集成电路的数据。数据基本上由紧密位压缩(大部分)big-endian 整数组成,其中一些(实际上很多)未与字节边界对齐 .所以,我尝试使用 FPC 的 bitpacked
记录来解决这个问题,但发现自己陷入了深深的麻烦中。
我尝试读取的第一个结构具有以下格式:
{$BITPACKING ON}
type
THeader = bitpacked record
Magic: Byte; // format id, 8 bits
_Type: [=10=]0..$FFF; // type specifier, 12 bits
Version: Word; // data revision, 16 bits
Flags: [=10=]..$F // attributes, 4 bits
end;
这是阅读代码:
procedure TForm1.FormCreate(Sender: TObject);
var
F: File;
Header: THeader;
begin
Writeln(SizeOf(Header), #9, BitSizeOf(Header)); // reports correctly
Writeln('SizeOf(Header._Type) = ', SizeOf(Header._Type)); // correctly reports 2 bytes
Writeln('BitSizeOf(Header._Type) = ', BitSizeOf(Header._Type)); // correctly reports 12 bits
AssignFile(F, 'D:fd8.dat');
FileMode := fmOpenRead;
Reset(F, SizeOf(Byte));
BlockRead(F, Header, SizeOf(Header));
{ data is incorrect beyond this point already }
//Header._Type := BEtoN(Header._Type);
Writeln(IntToHex(Header.Magic, SizeOf(Header.Magic) * 2));
Writeln(IntToHex(BEtoN(Header._Type), SizeOf(Header._Type) * 2));
Writeln(BEtoN(Header.Version));
end;
但是代码打印的数据完全错误。
这是手动完成的数据和解释:
0000000000: F1 55 BE 3F 0A ...
Magic = F1
_Type = 55B
Version = E3F0
Flags = A
但 FPC 以完全不同且不正确的方式查看数据。由于主机的小字节序,看起来属于字段的半字节(和位)不连续(例如:半字节 B
通常应该属于 _Type
字段和半字节 E
- 到Version
)。这是来自 Lazarus 的手表 window:
请指教我应该如何处理这种行为。这个non-contiguous bitfield是不是FPC的bug?任何可能的解决方法?
字节数
F1 55 BE 3F 0A
具有以下连续半字节(低半字节在高半字节之前):
1 F 5 5 E B F 3 A 0
如果将它们分别分组为 2、3、4 和 1 个半字节,您将得到:
1 F --> $F1
5 5 E --> $E55 // highest nibble last, so E is highest.
B F 3 A --> $A3FB // same again: A is highest nibble
0 --> [=12=]
这对应于您在 Watch window 中看到的结果,而不是您手动解码的结果。
现在,如果数据是大端数据,则您必须使用移位和掩码手动解码:
X.Magic := bytes[0];
X._Type := (bytes[1] shl 4) or (bytes[2] shr 4);
X.Version := ((bytes[2] and [=13=]F) shl 12) or
(bytes[3] shl 4) or
(bytes[4] shr 4);
X.Flags := bytes[4] and [=13=]F;
我用这个函数从IEEE格式转换成FPC格式:
Type MyReal = Array [1..4] of Byte;
function IeeeToSingle (src:MyReal):Single;
var x:MyReal;
s:single absolute x;
man:word;
exp:word;
ca:single;
cb:cardinal absolute ca; // 4 byte unsigned long int
begin
x:=src;
ca:=s;
if cb>0 then begin // not zero
man := cb shr 16;
exp := (man and $ff00) - 00;
if ((exp and 00) <> (man and 00)) then
MsToIeee := -1; // exponent overflow
man := (man and f) or ((man shl 8) and 00);// move sign
man := man or (exp shr 1);
cb := (cb and $ffff) or (Cardinal(man) shl 16);
end;
IeeeToSingle := ca;
end;
我正在尝试在 小端计算机 上使用 FreePascal 来读取和解释来自集成电路的数据。数据基本上由紧密位压缩(大部分)big-endian 整数组成,其中一些(实际上很多)未与字节边界对齐 .所以,我尝试使用 FPC 的 bitpacked
记录来解决这个问题,但发现自己陷入了深深的麻烦中。
我尝试读取的第一个结构具有以下格式:
{$BITPACKING ON}
type
THeader = bitpacked record
Magic: Byte; // format id, 8 bits
_Type: [=10=]0..$FFF; // type specifier, 12 bits
Version: Word; // data revision, 16 bits
Flags: [=10=]..$F // attributes, 4 bits
end;
这是阅读代码:
procedure TForm1.FormCreate(Sender: TObject);
var
F: File;
Header: THeader;
begin
Writeln(SizeOf(Header), #9, BitSizeOf(Header)); // reports correctly
Writeln('SizeOf(Header._Type) = ', SizeOf(Header._Type)); // correctly reports 2 bytes
Writeln('BitSizeOf(Header._Type) = ', BitSizeOf(Header._Type)); // correctly reports 12 bits
AssignFile(F, 'D:fd8.dat');
FileMode := fmOpenRead;
Reset(F, SizeOf(Byte));
BlockRead(F, Header, SizeOf(Header));
{ data is incorrect beyond this point already }
//Header._Type := BEtoN(Header._Type);
Writeln(IntToHex(Header.Magic, SizeOf(Header.Magic) * 2));
Writeln(IntToHex(BEtoN(Header._Type), SizeOf(Header._Type) * 2));
Writeln(BEtoN(Header.Version));
end;
但是代码打印的数据完全错误。
这是手动完成的数据和解释:
0000000000: F1 55 BE 3F 0A ...
Magic = F1
_Type = 55B
Version = E3F0
Flags = A
但 FPC 以完全不同且不正确的方式查看数据。由于主机的小字节序,看起来属于字段的半字节(和位)不连续(例如:半字节 B
通常应该属于 _Type
字段和半字节 E
- 到Version
)。这是来自 Lazarus 的手表 window:
请指教我应该如何处理这种行为。这个non-contiguous bitfield是不是FPC的bug?任何可能的解决方法?
字节数
F1 55 BE 3F 0A
具有以下连续半字节(低半字节在高半字节之前):
1 F 5 5 E B F 3 A 0
如果将它们分别分组为 2、3、4 和 1 个半字节,您将得到:
1 F --> $F1
5 5 E --> $E55 // highest nibble last, so E is highest.
B F 3 A --> $A3FB // same again: A is highest nibble
0 --> [=12=]
这对应于您在 Watch window 中看到的结果,而不是您手动解码的结果。
现在,如果数据是大端数据,则您必须使用移位和掩码手动解码:
X.Magic := bytes[0];
X._Type := (bytes[1] shl 4) or (bytes[2] shr 4);
X.Version := ((bytes[2] and [=13=]F) shl 12) or
(bytes[3] shl 4) or
(bytes[4] shr 4);
X.Flags := bytes[4] and [=13=]F;
我用这个函数从IEEE格式转换成FPC格式:
Type MyReal = Array [1..4] of Byte;
function IeeeToSingle (src:MyReal):Single;
var x:MyReal;
s:single absolute x;
man:word;
exp:word;
ca:single;
cb:cardinal absolute ca; // 4 byte unsigned long int
begin
x:=src;
ca:=s;
if cb>0 then begin // not zero
man := cb shr 16;
exp := (man and $ff00) - 00;
if ((exp and 00) <> (man and 00)) then
MsToIeee := -1; // exponent overflow
man := (man and f) or ((man shl 8) and 00);// move sign
man := man or (exp shr 1);
cb := (cb and $ffff) or (Cardinal(man) shl 16);
end;
IeeeToSingle := ca;
end;