Мне очень не нравится скорость побайтового чтения - 3МБ файл читается больше секунды, когда дельфовый аналог на BlockRead считывает его моментально.
Всё, что приходит в голову - открыть файл для типа byte, узнать размер, потом открыть файл для массива данного размера и считать за раз.
Byte_IO.Open(SF, Byte_IO.In_File, "test.txt");
declare
S: Byte_IO.Count := Byte_IO.Size(SF);
type Arr is array (1..S) of byte;
type AArr is access Arr;
package Arr_IO is new Ada.Direct_IO(Arr);
F: Arr_IO.File_Type;
A: AArr;
begin
A := new Arr;
Byte_IO.Close(SF);
Arr_IO.Open(F, Arr_IO.In_File, "test.txt");
Arr_IO.Read(F, A.all);
Arr_IO.Close(F);
end;
procedure File_Read isшестимегабайтный файл прочитало меньше, чем за 0.1 секунды.
Name : String := "F:\Programs\Ada\forum\debug\test.txt";
File_Size : Integer := Integer (Ada.Directories.Size (Name));
type Byte_Buffer is array(1 .. File_Size) of Byte;
package Arr_IO is new Ada.Direct_IO (Byte_Buffer);
File : Arr_IO.File_Type;
Buffer : Byte_Buffer;
begin
Arr_IO.Open (File, Arr_IO.In_File, Name);
Arr_IO.Read (File, Buffer);
Arr_IO.Close (File);
Ada.Text_IO.Put_Line (Integer'Image (File_Size));
end File_Read;
Так, я понял. При инициализации шаблона там, в модуле файлов, на стеке создаётся массив соответствующего размера.
> Не подтверждается... В пустом проекте читает трехмегабайтный файл легко...
А где в настройках компилятора размер стека правится?
Впрочем, это не решение.
А если я захочу в кучу прочитать полугиговый файл за раз?
Ну допустим, что это неправильно: я могу с той же скоростью читать порциями по 4096 байт, но это же привязывание к размеру кластера (т.е. к особенностям машины). Жаль, что стандартный файловый тип не умеет буферизовать (как я понял).
> Чтобы узнать размер файла, совсем не обязательно его открывать. Есть пакет Directories, который предоставляет тебе информацию:
Спасибо.
Всё-таки, каков оптимальный размер для считывания за раз? Может, есть стандартная функция, которая это говорит, по аналогии с функцией, говорящей оптимальный размер блока для выделения памяти в куче?
>Работай с потоками, они умеют.
Да только я не умею...
Из описания в книге я так и не понял, как должен выглядеть полный пример использования потока. Я не понял, как и что необходимо закрывать. Ну завёл я F: File_Type, ну открыл файл, ну написал S := Stream(F); В конце что делать с этими S и F? Если бы я работал только с F, то я бы просто написал Close, а тут что надо? С этим S надо что-то делать?
И ещё. Я не люблю вручную писать закрытие файла по исключению, и просто писать закрытие в конце тоже не люблю, я вообще считаю, что все подобные вещи должны быть завёрнуты в контролируемый объект (и тут простой РАИИ позволяет делать то, где навороченные, но недетерминированные ГЦ не помогут никак), вплоть до жёсткого контроля на уровне запрета компилятора.
Так вот, нету ли стандартного контролируемого потока?
with Interfaces;
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Streams.Stream_IO;
with Ada.Direct_IO;
procedure Main is
subtype Byte is Interfaces.Unsigned_8;
type Byte_Buffer is array(Integer range <>) of aliased Byte;
type PByte_Buffer is access Byte_Buffer;
subtype sz_Test_A is Byte_Buffer(1 .. 19);
subtype sz_Test_B is Ada.Streams.Stream_Element_Array(1 .. 19);
Is_Workaround_Possible: constant Boolean :=
sz_Test_A'Size = sz_Test_B'Size and sz_Test_A'Alignment = sz_Test_B'Alignment;
procedure BlockRead(File : in Ada.Streams.Stream_IO.File_Type;
Buffer : out Byte_Buffer;
Was_Read: out Natural) is
use Ada.Streams, Ada.Streams.Stream_IO;
SE_Buffer : Stream_Element_Array (1 .. buffer'Length);
for SE_Buffer'Address use Buffer'Address;
pragma Import (Ada, SE_Buffer);
Last_Read : Stream_Element_Offset;
begin
if Is_Workaround_Possible then
-- Быстрый путь
Read (Stream (file).all, SE_Buffer, Last_Read);
Was_Read := Natural (Last_Read);
else
-- Медленный путь
if End_Of_File(file) then
Was_Read := 0;
else
Was_Read :=
Integer'Min (Buffer'Length, Integer (Size (File) - Index (File) + 1));
Byte_Buffer'Read (Stream (File),
Buffer (Buffer'First .. Buffer'First + Was_Read - 1));
end if;
end if;
end BlockRead;
File_Name : String := "F:\Programs\Ada\forum\debug\aga.txt";
SFIn : Ada.Streams.Stream_IO.File_Type;
Buffer : PByte_Buffer;
Buf_Size : Integer;
Bytes_Read : Integer;
begin
-- Открыл
Ada.Streams.Stream_IO.Open(SFIn, Ada.Streams.Stream_IO.In_File, File_Name);
-- Получил размер
Buf_Size := Integer (Ada.Streams.Stream_IO.Size (SFIn));
-- Выделил память
Buffer := new Byte_Buffer (1 .. Buf_Size);
-- Прочел (это - процедура Gautier)
BlockRead (SFIn, Buffer.all, Bytes_Read);
-- Проверил, все ли нормально
if Buf_Size /= Bytes_Read then
Ada.Text_IO.Put_Line ("Something goes wrong ...");
end if;
-- Закрыл
Ada.Streams.Stream_IO.Close(SFIn);
end Main;
А почему именно от 1 до 19? Специально, чтобы не делилось на 4?
Дальше, умеет ли компилятор определять значение Is_Workaround_Possible, убрав одну из ветвей
if Is_Workaround_Possible then
...
else
...
end if;
> Ни файлов, ни потоков контролируемых нет и не будет.
То есть в стандартной библиотеке не будет?
Себе я сделал так:
with Ada.Streams.Stream_IO;
with Ada.Finalization;
package Controlled_Streams is
type Controlled_Stream_IO is new Ada.Finalization.Limited_Controlled with record
F: Ada.Streams.Stream_IO.File_Type;
S: Ada.Streams.Stream_IO.Stream_Access;
end record;
function New_Stream_IO(Mode : Ada.Streams.Stream_IO.File_Mode;
Name : String;
Form : String := "") return Controlled_Stream_IO;
private
pragma Warnings(Off);
overriding procedure Initialize (CS_IO: in out Controlled_Stream_IO);
overriding procedure Finalize (CS_IO: in out Controlled_Stream_IO);
pragma Warnings(On);
end Controlled_Streams;
package body Controlled_Streams is
overriding procedure Initialize (CS_IO: in out Controlled_Stream_IO) is begin
null;
end;
overriding procedure Finalize (CS_IO: in out Controlled_Stream_IO) is begin
if Ada.Streams.Stream_IO.Is_Open(CS_IO.F) then
Ada.Streams.Stream_IO.Close(CS_IO.F);
end if;
end;
function New_Stream_IO(Mode : Ada.Streams.Stream_IO.File_Mode;
Name : String;
Form : String := "") return Controlled_Stream_IO is begin
return CS_IO: Controlled_Stream_IO do
CS_IO.S := null;
Ada.Streams.Stream_IO.Open(CS_IO.F, Mode, Name, Form);
CS_IO.S := Ada.Streams.Stream_IO.Stream(CS_IO.F);
end return;
end;
end Controlled_Streams;
declare CS: Controlled_Stream_IO := New_Stream_IO(In_File, "test.txt");
begin
тут работаем, про исключения и необходимость закрыть не думаем
end;
subtype mysz_Test_A is Byte_Buffer(1 .. Buffer'Length);, и проверяй размер/выравнивание этих подтипов. У меня при подключении твоего пакета без сбоя прочитались 3-х, 6-ти и 9-ти Мб файлы. И не только прочитались, а и скопировались в другой стрим (ну да, я добавил еще создание файла в твой код). Хотя, если тебя скорость устраивает... Может, в 2010 уже починили эту багу просто? в GNAT-2009 с этим невозможно было мириться.
subtype mysz_Test_B is Ada.Streams.Stream_Element_Array(1 .. buffer'Length);
Код такой:
procedure Load_From_File(V: out Vector; File_Name: String) is
use Ada.Streams;
use Ada.Streams.Stream_IO;
File : constant Controlled_Stream_IO := New_Stream_IO(In_File, File_Name);
El_Count : constant integer := integer(Size(File.F)) * 8 / Element'Size;
subtype byte is Interfaces.Unsigned_8;
subtype Test_SA is Stream_Element_Array
(1 .. Stream_Element_Offset(El_Count * Element'Size));
type Test_BA is array
(1 .. Stream_Element_Offset(El_Count * Element'Size)) of aliased byte;
Is_Workaround_Possible: constant Boolean := (Test_SA'Size = Test_BA'Size) and (Test_SA'Alignment = Test_BA'Alignment);
begin
V.Set_Length(0);
V.Set_Length(El_Count);
if Is_Workaround_Possible then
declare
Buffer : Test_SA;
for Buffer'Address use V.Elements.all'Address;
Last : Stream_Element_Offset; pragma Unreferenced(Last);
begin
Read(File.S.all, Buffer, Last);
end;
else
Element_Array'Read(File.S, V.Elements.all (V.First .. V.Last));
end if;
end;