Adaでファイルの内容をLittleEndian32BitIntegerで読み込む

所用でファイルから32bitもしくは16bitの整数をLittleEndian固定で読み込む必要があったのだが、意外と面倒だった。

with Ada.Streams.Stream_IO, Ada.Text_IO, Ada.Integer_Text_IO;
use type Ada.Streams.Stream_Element;

procedure Sample is

   type Little_Endian_Unsigned_32bit is mod ((2**8)**4);
   type Little_Endian_Unsigned_16bit is mod ((2**8)**2);

   procedure Little_Endian_Unsigned_32bit_Read(Stream : access Ada.Streams.Root_Stream_Type'Class; Item : out Little_Endian_Unsigned_32bit'Base);
   for Little_Endian_Unsigned_32bit'Read use Little_Endian_Unsigned_32bit_Read;

   procedure Little_Endian_Unsigned_16bit_Read(Stream : access Ada.Streams.Root_Stream_Type'Class; Item : out Little_Endian_Unsigned_16bit'Base);
   for Little_Endian_Unsigned_16bit'Read use Little_Endian_Unsigned_16bit_Read;

   procedure Little_Endian_Unsigned_32bit_Read(Stream : access Ada.Streams.Root_Stream_Type'Class; Item : out Little_Endian_Unsigned_32bit'Base) is
      Element_Array : Ada.Streams.Stream_Element_Array(1..4);
      Last : Ada.Streams.Stream_Element_Offset;
   begin
      Stream.Read(Item => Element_Array, Last => Last);
      Item := (Little_Endian_Unsigned_32bit(Element_Array(1)) * ((2**8)**0))
        + (Little_Endian_Unsigned_32bit(Element_Array(2)) * ((2**8)**1))
        + (Little_Endian_Unsigned_32bit(Element_Array(3)) * ((2**8)**2))
        + (Little_Endian_Unsigned_32bit(Element_Array(4)) * ((2**8)*3));
   end Little_Endian_Unsigned_32bit_Read;

   procedure Little_Endian_Unsigned_16bit_Read(Stream : access Ada.Streams.Root_Stream_Type'Class; Item : out Little_Endian_Unsigned_16bit'Base) is
      Element_Array : Ada.Streams.Stream_Element_Array(1..2);
      Last : Ada.Streams.Stream_Element_Offset;
   begin
      Stream.Read(Item => Element_Array, Last => Last);
      Item := (Little_Endian_Unsigned_16bit(Element_Array(1)) * ((2**8)**0))
        + (Little_Endian_Unsigned_16bit(Element_Array(2)) * ((2**8)**1));
   end Little_Endian_Unsigned_16bit_Read;

   Raw_File : Ada.Streams.Stream_IO.File_Type;
   Raw_File_Stream : Ada.Streams.Stream_IO.Stream_Access;
   Buffer_32bit : Little_Endian_Unsigned_32bit;
   Buffer_16bit : Little_Endian_Unsigned_16bit; 

begin
   -- ファイルを開く
   Ada.Streams.Stream_IO.Open(File => Raw_File,
                              Mode => Ada.Streams.Stream_IO.In_File,
                              Name => "path/to/file/_MG_8912.CR2");

   Raw_File_Stream := Ada.Streams.Stream_IO.Stream(Raw_File);

   Little_Endian_Unsigned_32bit'Read(Raw_File_Stream, Buffer_32bit);
   Ada.Integer_Text_IO.Put(Integer(Buffer_32bit));
   Ada.Text_IO.New_Line;

   Little_Endian_Unsigned_16bit'Read(Raw_File_Stream, Buffer_16bit);
   Ada.Integer_Text_IO.Put(Integer(Buffer_16bit));
   Ada.Text_IO.New_Line;
end Sample;

Adaには標準でpack関数がない(というか、見つからなかった)ので(この場合unpackだが)、LittleEndian固定でバイト列を読み出す関数を自作する必要があった。が、バイト列の計算部分が今一綺麗にならない。Ada.Streams.Stream_Elementmod型なので計算する際は型変換する必要があるのだが、どうしてもコードが冗長っぽくなってしまう。もうちょっとこう、ビット演算ぽい感じで簡単にできないものだろうか。うーむ。

それにしても、T'Attribute(..., Item : T)な定義を使うべきかで悩む。ドット記法が出来るようになった時代としては、そちらに統一したい感じもするが……。