You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ada语言中Tile'Read读取字节流至记录时出现异常求助

自定义Ada记录类型读取文件异常排查

类型定义

type U32        is new Interfaces.Unsigned_32;
type U16        is new Interfaces.Unsigned_16;
type U8         is new Interfaces.Unsigned_8;
type Rotation_t is range 0 .. 3             with Size => 2;
type Tilenum_t  is new Interfaces.Unsigned_8;
type Padding_t  is range 0 .. 7             with Size => 3;

type Tile is record
   Tilenum :    Tilenum_t;
   XFlipped :   Boolean;
   YFlipped :   Boolean;
   Rotation :   Rotation_t;
   TriFlipped : Boolean;
   Padding :    Padding_t;
   Height :     U8;
end record with Size => 24;

for Tile use
record
   --  first byte
   Tilenum    at 0 range 0 .. 7;       -- 1111 1111   0000 0000   0000 0000
   --  second byte
   XFlipped   at 0 range 8 .. 8;       -- 0000 0000   1000 0000   0000 0000
   YFlipped   at 0 range 9 .. 9;       -- 0000 0000   0100 0000   0000 0000
   Rotation   at 0 range 10 .. 11;     -- 0000 0000   0011 0000   0000 0000
   TriFlipped at 0 range 12 .. 12;     -- 0000 0000   0000 1000   0000 0000
   Padding    at 0 range 13 .. 15;     -- 0000 0000   0000 0111   0000 0000
   --  third byte
   Height     at 0 range 16 .. 23;     -- 0000 0000   0000 0000   1111 1111
end record;
for Tile'Bit_Order            use System.Low_Order_First;
for Tile'Scalar_Storage_Order use System.Low_Order_First;

磁盘数据

数据以3字节为一组存储,磁盘十六进制dump结果(仅关注第二行数据):

# hexdump -C
# we are only interested in second line: first one is metadata
00000000  6d 61 70 20 0a 00 00 00  65 00 00 00 50 00 00 00  |map ....e...P...|
00000010  11 00 00 11 00 00 11 00  00 11 00 00 11 00 00 11  |................|

异常读取结果

使用Tile'Read (...)读取后得到不符合预期的翻转结果:

# ada reads:
Tilenum= 17 XFlipped=FALSE YFlipped=FALSE Rotation= 1 TriFlipped=FALSE Padding= 0 Height= 17
Tilenum= 0  XFlipped=FALSE YFlipped=TRUE  Rotation= 0 TriFlipped=FALSE Padding= 1 Height= 0
Tilenum= 0  XFlipped=TRUE  YFlipped=FALSE Rotation= 0 TriFlipped=TRUE  Padding= 0 Height= 0
Tilenum= 17 XFlipped=FALSE YFlipped=FALSE Rotation= 1 TriFlipped=FALSE Padding= 0 Height= 17
# same as above, in numerical form:
0x11 [b:0001 0000] 0x11
0x00 [b:0100 0001] 0x00
0x00 [b:1000 1000] 0x00
0x11 [b:0001 0000] 0x11

预期结果

0x11 0 0
0x11 0 0
0x11 0 0
...

问题分析与解决

问题核心是位序设置与磁盘数据的实际存储位序不匹配:

  • 当前设置Tile'Bit_Order use System.Low_Order_First,会让Ada将每个字节内的位从最低位开始解析,但磁盘中字节的位序是标准的高序位在前(High_Order_First)。
  • 以磁盘中的0x11(二进制00010001)为例,Low_Order_First会将其视为10001000,导致原本的位被反转拆分,映射到错误的字段中。

修复方案

  1. 调整位序设置
    将类型定义末尾的位序改为System.High_Order_First,匹配磁盘数据的字节内位序:
for Tile'Bit_Order            use System.High_Order_First;
for Tile'Scalar_Storage_Order use System.Low_Order_First;
  1. 手动解析数据(可选)
    如果需要完全控制解析逻辑,可以绕过Ada自动序列化,手动读取字节并解析位:
procedure Read_Tile (File : in out Ada.Streams.Stream_IO.File_Type; Result : out Tile) is
   Buffer : array (1..3) of U8;
begin
   U8'Read (Ada.Streams.Stream_IO.Stream (File), Buffer(1));
   U8'Read (Ada.Streams.Stream_IO.Stream (File), Buffer(2));
   U8'Read (Ada.Streams.Stream_IO.Stream (File), Buffer(3));
   
   Result.Tilenum := Tilenum_t(Buffer(1));
   Result.XFlipped := (Buffer(2) and 16#80#) /= 0;
   Result.YFlipped := (Buffer(2) and 16#40#) /= 0;
   Result.Rotation := Rotation_t(Shift_Right(Buffer(2) and 16#30#, 4));
   Result.TriFlipped := (Buffer(2) and 16#08#) /= 0;
   Result.Padding := Padding_t(Buffer(2) and 16#07#);
   Result.Height := Buffer(3);
end Read_Tile;

内容的提问来源于stack exchange,提问作者Denis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 08:35:24