使用 Ada.Streams.Stream_IO.Read 的最大流文件 size/offset

Maximum size/offset of streamed File using Ada.Streams.Stream_IO.Read

我尝试从一个(可能)巨大的文件中读取特定的数据块(大约 4096 字节)。

在 GNAT 编译器中使用 Ada.Streams.Stream_IO.Read(),我可以使用的最大偏移量是多少?也就是说,如果我想读取文件的最后 4 KB,使用

type Block_Array is array (1..4096) of Positive;
...
Ada.Streams.Stream_IO.Read(File, Block_Array, Last, Offset);

Offset 有多大(因此文件有多大)?

做一些研究,Offset 在 GNAT 中似乎被定义为 2 ** mod Standard'Address_Size [1],在 32 位机器上将是 2^32。我不太清楚这是指位、字节、千字节还是一些晦涩的倍数。

假设它表示字节,这是否意味着我可以处理的最大文件是 32 GB ((2^32*8)/1024^3)?如果是这样,有没有办法让它变大?


由于建议我没有查看(语言)参考手册,这里是首先导致我提出问题的研究:

在 [2] 中,read 过程定义为:

procedure Read (File : in  File_Type;
                Item : out Stream_Element_Array;
                Last : out Stream_Element_Offset;
                From : in  Positive_Count);

再往上一点:

type    Count          is range 0 .. *implementation-defined*;
subtype Positive_Count is Count range 1 .. Count'Last;

可以看出,Count的实际范围是实现定义的。因为我使用的是 GNAT 编译器(见上文),所以我检查了 [1]。这说明

The Standard I/O packages described in Annex A for [...] Ada.Stream_IO [...] are implemented using the C library streams facility; where [...] All input/output operations use fread/fwrite.

在下面的同一文档中

function fread
     (buffer : voids;
      size : size_t;
      count : size_t;
      stream : FILEs)

其中

type size_t is mod 2 ** Standard'Address_Size;

同样,Standard'Address_Size 在 32 位机器上将是 32(在询问我的计算机上是否属于这种情况之前,我也进行了检查)。在阅读了语言参考手册和 GNAT 的实现文档后,我仍然不确定 Stream_Element_Offset 是指字节还是其他内容。

但是再次假设它表示字节,这是否意味着我可以处理的最大文件是 32 GB ((2^32*8)/1024^3)?如果是这样,有没有办法让它变大?

[1]: The Implementation of Standard I/O - GNAT Reference Manual

[2]: Ada Reference Manual - A.12.1 The Package Streams.Stream_IO

在 Mac OS X 上,使用 FSF GCC 5.1.0,有

procedure Read
  (File : File_Type;
   Item : out Stream_Element_Array;
   Last : out Stream_Element_Offset;
   From : Positive_Count);

哪里

type Count is new Stream_Element_Offset
  range 0 .. Stream_Element_Offset’Last;

subtype Positive_Count is Count range 1 .. Count’Last;            --'
--  Index into file, in stream elements

和(在Ada.Streams

type Stream_Element_Offset is new Long_Long_Integer;

这是 64 位..应该足够了。

但是,正如 Alex 指出的那样,GNAT GPL 2014 有

type Stream_Element_Offset is range
  -(2 ** (Standard'Address_Size - 1)) ..
  +(2 ** (Standard'Address_Size - 1)) - 1;

这意味着,在 32 位计算机上,您被限制为 2 GB 的文件。

最新的 FSF GCC 源代码(与上面的 5.1.0 一样)已经更改;我们必须等到 GNAT GPL 2015 才能确定哪个是最终的。

更令人担忧的是,Ada.Streams.Stream_IO.Set_Position(内部子程序)的 GNAT GPL 2014 代码是

procedure Set_Position (File : File_Type) is
   use type System.CRTL.long;
   use type System.CRTL.ssize_t;
   R : int;
begin
   if Standard'Address_Size = 64 then
      R := fseek64 (File.Stream,
                    System.CRTL.ssize_t (File.Index) - 1, SEEK_SET);
   else
      R := fseek (File.Stream,
                  System.CRTL.long (File.Index) - 1, SEEK_SET);
   end if;

   if R /= 0 then
      raise Use_Error;
   end if;
end Set_Position;

而 GCC 5.1.0 版本(没有替代实现)是

procedure Set_Position (File : File_Type) is
   use type System.CRTL.int64;
   R : int;
begin
   R := fseek64 (File.Stream, System.CRTL.int64 (File.Index) - 1, SEEK_SET);

   if R /= 0 then
      raise Use_Error;
   end if;
end Set_Position;

如果您的系统有 fseek64() - 或者可能 fseeko(),它需要一个 off_t 而不是 long 作为 offset 参数 - 和朋友(我认为它必须,看看上面的代码)我认为编写自己的 Ada.Streams.Stream_IO 版本以始终使用 64 位函数并不会太难。可能最简单的方法是将其命名为 My_Stream_IO 并咬紧牙关关于使用内部 GNAT 单元的编译器警告,而不是尝试将其放入 Ada 层次结构中。

如果您想知道标量类型的范围,可以使用 <type>'First<type>'Last:

访问最小值和最大值
with Ada.Text_IO;

procedure Check_Range is
   use Ada.Text_IO;
   subtype Type_To_Check is Integer;
begin
   Put (Type_To_Check'Image (Type_To_Check'First));
   Put (" .. ");
   Put (Type_To_Check'Image (Type_To_Check'Last));
   New_Line;
end Check_Range;

如果您想访问比标准库支持的更大的文件,您可以导入操作系统函数以访问操作系统允许的最大文件。

从其他语言导入函数的详细信息在语言参考手册的附件 B 中指定。