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

如何在Ada中以BigEndian序读取二进制文件至记录?

解决BigEndian二进制文件读取问题及代码优化

问题根源

默认情况下,Ada的流操作(如'Read)会使用主机系统的字节序解析多字节标量类型(比如Unsigned_32),忽略记录类型的Scalar_Storage_Order设置。此外,你提供的Share_Header记录表示子句不完整(仅定义了Version字段),这会导致编译错误或未定义行为,因为Ada要求记录表示子句必须覆盖所有组件。

解决方案:自定义BigEndian读取逻辑

通过重载Share_Header的'Read操作,手动按BigEndian顺序读取每个字段,确保数据解析正确。

修正后的包规范(share.ads)

with Interfaces; use Interfaces;
with Ada.Streams;

package Share is

   type Byte is new Interfaces.Unsigned_8;
   type Kilobyte is array (Integer range 0 .. 1_023) of Byte;
   type Kilobyte_array is array (Integer range <>) of Kilobyte;

   type Share_Header is record
      Version                    : Unsigned_32;
      Data_Length                : Unsigned_32;
      Lease_number               : Unsigned_32;
      Version_Junk               : Unsigned_32;
      Block_Size                 : Unsigned_32;
      Data_Size                  : Unsigned_32;
      Data_Offset                : Unsigned_32;
      Plaintext_Hash_Tree_Offset : Unsigned_32;
      Crypttext_Hash_Tree_Offset : Unsigned_32;
      Block_Hashes_Offset        : Unsigned_32;
      Share_Hashes_Offset        : Unsigned_32;
      URI_Extension_Offset       : Unsigned_32;
   end record;

   -- 重载Read操作以处理BigEndian格式
   procedure Read (S : not null access Ada.Streams.Root_Stream_Type'Class;
                   Item : out Share_Header);
   for Share_Header'Read use Read;

   procedure Read_Share (Segment_Size : Positive; Required_Shares : Positive);
   procedure Display_Share_Content (My_Share_Header : Share_Header);
   procedure Read_Blocks
     (My_Share_Header : Share_Header;
      Share_File      : Ada.Streams.Stream_IO.File_Type);

end Share;

修正后的包体(share.adb)

with Ada.Text_IO;
with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO;
with Ada.Streams;           use Ada.Streams;

package body Share is

   -- 辅助函数:从流中读取BigEndian格式的Unsigned_32
   function Read_BE_U32 (S : not null access Root_Stream_Type'Class) return Unsigned_32 is
      B1, B2, B3, B4 : Byte;
   begin
      Byte'Read (S, B1);
      Byte'Read (S, B2);
      Byte'Read (S, B3);
      Byte'Read (S, B4);
      return Unsigned_32(B1) * 2**24 +
             Unsigned_32(B2) * 2**16 +
             Unsigned_32(B3) * 2**8 +
             Unsigned_32(B4);
   end Read_BE_U32;

   -- 重载Share_Header的Read操作
   procedure Read (S : not null access Root_Stream_Type'Class;
                   Item : out Share_Header) is
   begin
      Item.Version                    := Read_BE_U32(S);
      Item.Data_Length                := Read_BE_U32(S);
      Item.Lease_number               := Read_BE_U32(S);
      Item.Version_Junk               := Read_BE_U32(S);
      Item.Block_Size                 := Read_BE_U32(S);
      Item.Data_Size                  := Read_BE_U32(S);
      Item.Data_Offset                := Read_BE_U32(S);
      Item.Plaintext_Hash_Tree_Offset := Read_BE_U32(S);
      Item.Crypttext_Hash_Tree_Offset := Read_BE_U32(S);
      Item.Block_Hashes_Offset        := Read_BE_U32(S);
      Item.Share_Hashes_Offset        := Read_BE_U32(S);
      Item.URI_Extension_Offset       := Read_BE_U32(S);
   end Read;

   procedure Read_Share (Segment_Size : Positive; Required_Shares : Positive)
   is
      -- 向上取整计算块大小
      Block_Size : constant Positive :=
        (Segment_Size + (Required_Shares - 1)) / Required_Shares;

      S               : Stream_Access;
      Share_File      : File_Type;
      My_Share_Header : Share_Header;
   begin
      Open (Share_File, In_File, "../go-tahoe/3");
      S := Stream (Share_File);
      Share_Header'Read (S, My_Share_Header);
      -- 覆盖计算得到的块大小
      My_Share_Header.Block_Size := Unsigned_32 (Block_Size);
      Display_Share_Content (My_Share_Header);

      -- 读取块数据
      Read_Blocks (My_Share_Header, Share_File);

      Close (Share_File);
   end Read_Share;

   -- 辅助过程:打印Unsigned_32类型的字段
   procedure Put_U32 (Label : String; Value : Unsigned_32) is
   begin
      Ada.Text_IO.Put_Line (Label & ": " & Unsigned_32'Image(Value));
   end Put_U32;

   procedure Display_Share_Content (My_Share_Header : Share_Header) is
   begin
      Put_U32("Share version", My_Share_Header.Version);
      Put_U32("Share Data Length", My_Share_Header.Data_Length);
      Put_U32("Lease Number", My_Share_Header.Lease_number);
      Put_U32("Version Junk", My_Share_Header.Version_Junk);
      Put_U32("Block Size", My_Share_Header.Block_Size);
      Put_U32("Data Size", My_Share_Header.Data_Size);
      Put_U32("Data offset", My_Share_Header.Data_Offset);
      Put_U32("Plaintext hash tree offset", My_Share_Header.Plaintext_Hash_Tree_Offset);
      Put_U32("Crypttext hash tree offset", My_Share_Header.Crypttext_Hash_Tree_Offset);
      Put_U32("Block hashes offset", My_Share_Header.Block_Hashes_Offset);
      Put_U32("Share hashes offset", My_Share_Header.Share_Hashes_Offset);
      Put_U32("URI Extension offset", My_Share_Header.URI_Extension_Offset);
   end Display_Share_Content;

   procedure Read_Blocks
     (My_Share_Header : Share_Header; Share_File : File_Type)
   is
      Total_Bytes : constant Unsigned_32 := My_Share_Header.Data_Size;
      Block_Size  : constant Unsigned_32 := My_Share_Header.Block_Size;
      Remaining   : Unsigned_32 := Total_Bytes;
      Current_Pos : Unsigned_32 := My_Share_Header.Data_Offset;
      S : Stream_Access := Stream(Share_File);
   begin
      -- 定位到数据起始位置(Stream_IO索引从1开始)
      Set_Index(Share_File, Positive(Current_Pos + 1));

      while Remaining > 0 loop
         declare
            Read_Size : constant Natural := Natural(Unsigned_32'Min(Remaining, Block_Size));
            Partial_Block : Byte_Array(1 .. Read_Size);
         begin
            Byte_Array'Read(S, Partial_Block);
            -- 示例:打印块数据前4字节(可替换为实际业务逻辑)
            if Read_Size >=4 then
               Ada.Text_IO.Put_Line("Block data (first 4 bytes): " &
                  Byte'Image(Partial_Block(1)) & ", " &
                  Byte'Image(Partial_Block(2)) & ", " &
                  Byte'Image(Partial_Block(3)) & ", " &
                  Byte'Image(Partial_Block(4)));
            end if;
            Remaining := Remaining - Unsigned_32(Read_Size);
         end;
      end loop;
   end Read_Blocks;

end Share;

代码优化建议

  1. 消除重复代码:使用辅助过程Put_U32统一处理Unsigned_32字段的打印,减少冗余。
  2. 完善块读取逻辑:实现了基于偏移量和大小的块读取,支持处理剩余不足一个块的情况。
  3. 移除无效代码:删除不完整的记录表示子句,避免编译错误。
  4. 异常处理:建议添加Ada.IO_Exceptions异常处理,处理文件打开失败、读取错误等情况,增强程序健壮性。
  5. 类型安全:使用Natural和Positive替代Integer表示大小和索引,确保数值非负。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 19:10:55