1------------------------------------------------------------------------------
2--                                                                          --
3--                         GNAT RUN-TIME COMPONENTS                         --
4--                                                                          --
5--                          S Y S T E M . M M A P                           --
6--                                                                          --
7--                                 S p e c                                  --
8--                                                                          --
9--                     Copyright (C) 2007-2018, AdaCore                     --
10--                                                                          --
11-- This library is free software;  you can redistribute it and/or modify it --
12-- under terms of the  GNU General Public License  as published by the Free --
13-- Software  Foundation;  either version 3,  or (at your  option) any later --
14-- version. This library is distributed in the hope that it will be useful, --
15-- but WITHOUT ANY WARRANTY;  without even the implied warranty of MERCHAN- --
16-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE.                            --
17--                                                                          --
18-- As a special exception under Section 7 of GPL version 3, you are granted --
19-- additional permissions described in the GCC Runtime Library Exception,   --
20-- version 3.1, as published by the Free Software Foundation.               --
21--                                                                          --
22-- You should have received a copy of the GNU General Public License and    --
23-- a copy of the GCC Runtime Library Exception along with this program;     --
24-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25-- <http://www.gnu.org/licenses/>.                                          --
26--                                                                          --
27-- GNAT was originally developed  by the GNAT team at  New York University. --
28-- Extensive contributions were provided by Ada Core Technologies Inc.      --
29--                                                                          --
30------------------------------------------------------------------------------
31
32--  This package provides memory mapping of files. Depending on your operating
33--  system, this might provide a more efficient method for accessing the
34--  contents of files.
35--  A description of memory-mapping is available on the sqlite page, at:
36--      http://www.sqlite.org/mmap.html
37--
38--  The traditional method for reading a file is to allocate a buffer in the
39--  application address space, then open the file and copy its contents. When
40--  memory mapping is available though, the application asks the operating
41--  system to return a pointer to the requested page, if possible. If the
42--  requested page has been or can be mapped into the application address
43--  space, the system returns a pointer to that page for the application to
44--  use without having to copy anything. Skipping the copy step is what makes
45--  memory mapped I/O faster.
46--
47--  When memory mapping is not available, this package automatically falls
48--  back to the traditional copy method.
49--
50--  Example of use for this package, when reading a file that can be fully
51--  mapped
52--
53--  declare
54--     File : Mapped_File;
55--     Str  : Str_Access;
56--  begin
57--     File := Open_Read ("/tmp/file_on_disk");
58--     Read (File);  --  read the whole file
59--     Str := Data (File);
60--     for S in 1 .. Last (File) loop
61--         Put (Str (S));
62--     end loop;
63--     Close (File);
64--  end;
65--
66--  When the file is big, or you only want to access part of it at a given
67--  time, you can use the following type of code.
68
69--  declare
70--     File   : Mapped_File;
71--     Str    : Str_Access;
72--     Offs   : File_Size := 0;
73--     Page   : constant Integer := Get_Page_Size;
74--  begin
75--     File := Open_Read ("/tmp/file_on_disk");
76--     while Offs < Length (File) loop
77--         Read (File, Offs, Length => Long_Integer (Page) * 4);
78--         Str := Data (File);
79--
80--         --  Print characters for this chunk:
81--         for S in Integer (Offs - Offset (File)) + 1 .. Last (File) loop
82--            Put (Str (S));
83--         end loop;
84--
85--         --  Since we are reading multiples of Get_Page_Size, we can simplify
86--         --  with
87--         --    for S in 1 .. Last (File) loop ...
88--
89--         Offs := Offs + Long_Integer (Last (File));
90--     end loop;
91
92with Interfaces.C;
93
94with System.Strings;
95
96package System.Mmap is
97
98   type Mapped_File is private;
99   --  File to be mapped in memory.
100
101   --  This package will use the fastest possible algorithm to load the
102   --  file in memory. On systems that support it, the file is not really
103   --  loaded in memory. Instead, a call to the mmap() system call (or
104   --  CreateFileMapping()) will keep the file on disk, but make it
105   --  accessible as if it was in memory.
106
107   --  When the system does not support it, the file is actually loaded in
108   --  memory through calls to read(), and written back with write() when you
109   --  close it. This is of course much slower.
110
111   --  Legacy: each mapped file has a "default" mapped region in it.
112
113   type Mapped_Region is private;
114   --  A representation of part of a file in memory. Actual reading/writing
115   --  is done through a mapped region. After being returned by Read, a mapped
116   --  region must be free'd when done. If the original Mapped_File was open
117   --  for reading, it can be closed before the mapped region is free'd.
118
119   Invalid_Mapped_File : constant Mapped_File;
120   Invalid_Mapped_Region : constant Mapped_Region;
121
122   type Unconstrained_String is new String (Positive);
123   type Str_Access is access all Unconstrained_String;
124   pragma No_Strict_Aliasing (Str_Access);
125
126   type File_Size is new Interfaces.C.size_t;
127
128   function To_Str_Access
129     (Str : System.Strings.String_Access) return Str_Access;
130   --  Convert Str. The returned value points to the same memory block, but no
131   --  longer includes the bounds, which you need to manage yourself
132
133   function Open_Read
134     (Filename              : String;
135      Use_Mmap_If_Available : Boolean := True) return Mapped_File;
136   --  Open a file for reading. The same file can be shared by multiple
137   --  processes, that will see each others's changes as they occur.
138   --  Any attempt to write the data might result in a segmentation fault,
139   --  depending on how the file is open.
140   --  Name_Error is raised if the file does not exist.
141   --  Filename should be compatible with the filesystem.
142
143   function Open_Read_No_Exception
144     (Filename              : String;
145      Use_Mmap_If_Available : Boolean := True) return Mapped_File;
146   --  Like Open_Read but return Invalid_Mapped_File in case of error
147
148   function Open_Write
149     (Filename              : String;
150      Use_Mmap_If_Available : Boolean := True) return Mapped_File;
151   --  Open a file for writing.
152   --  You cannot change the length of the file.
153   --  Name_Error is raised if the file does not exist
154   --  Filename should be compatible with the filesystem.
155
156   procedure Close (File : in out Mapped_File);
157   --  Close the file, and unmap the memory that is used for the region
158   --  contained in File. If the system does not support the unmmap() system
159   --  call or equivalent, or these were not available for the file itself,
160   --  then the file is written back to the disk if it was opened for writing.
161
162   procedure Free (Region : in out Mapped_Region);
163   --  Unmap the memory that is used for this region and deallocate the region
164
165   procedure Read
166     (File   : Mapped_File;
167      Region : in out Mapped_Region;
168      Offset : File_Size := 0;
169      Length : File_Size := 0;
170      Mutable : Boolean := False);
171   --  Read a specific part of File and set Region to the corresponding mapped
172   --  region, or re-use it if possible.
173   --  Offset is the number of bytes since the beginning of the file at which
174   --  we should start reading. Length is the number of bytes that should be
175   --  read. If set to 0, as much of the file as possible is read (presumably
176   --  the whole file unless you are reading a _huge_ file).
177   --  Note that no (un)mapping is is done if that part of the file is already
178   --  available through Region.
179   --  If the file was opened for writing, any modification you do to the
180   --  data stored in File will be stored on disk (either immediately when the
181   --  file is opened through a mmap() system call, or when the file is closed
182   --  otherwise).
183   --  Mutable is processed only for reading files. If set to True, the
184   --  data can be modified, even through it will not be carried through the
185   --  underlying file, nor it is guaranteed to be carried through remapping.
186   --  This function takes care of page size alignment issues. The accessors
187   --  below only expose the region that has been requested by this call, even
188   --  if more bytes were actually mapped by this function.
189   --  TODO??? Enable to have a private copy for readable files
190
191   function Read
192     (File    : Mapped_File;
193      Offset  : File_Size := 0;
194      Length  : File_Size := 0;
195      Mutable : Boolean := False) return Mapped_Region;
196   --  Likewise, return a new mapped region
197
198   procedure Read
199     (File    : Mapped_File;
200      Offset  : File_Size := 0;
201      Length  : File_Size := 0;
202      Mutable : Boolean := False);
203   --  Likewise, use the legacy "default" region in File
204
205   function Length (File : Mapped_File) return File_Size;
206   --  Size of the file on the disk
207
208   function Offset (Region : Mapped_Region) return File_Size;
209   --  Return the offset, in the physical file on disk, corresponding to the
210   --  requested mapped region. The first byte in the file has offest 0.
211
212   function Offset (File : Mapped_File) return File_Size;
213   --  Likewise for the region contained in File
214
215   function Last (Region : Mapped_Region) return Integer;
216   --  Return the number of requested bytes mapped in this region. It is
217   --  erroneous to access Data for indices outside 1 .. Last (Region).
218   --  Such accesses may cause Storage_Error to be raised.
219
220   function Last (File : Mapped_File) return Integer;
221   --  Return the number of requested bytes mapped in the region contained in
222   --  File. It is erroneous to access Data for indices outside of 1 .. Last
223   --  (File); such accesses may cause Storage_Error to be raised.
224
225   function Data (Region : Mapped_Region) return Str_Access;
226   pragma Inline (Data);
227   --  The data mapped in Region as requested. The result is an unconstrained
228   --  string, so you cannot use the usual 'First and 'Last attributes.
229   --  Instead, these are respectively 1 and Size.
230
231   function Data (File : Mapped_File) return Str_Access;
232   pragma Inline (Data);
233   --  Likewise for the region contained in File
234
235   function Is_Mutable (Region : Mapped_Region) return Boolean;
236   --  Return whether it is safe to change bytes in Data (Region). This is true
237   --  for regions from writeable files, for regions mapped with the "Mutable"
238   --  flag set, and for regions that are copied in a buffer. Note that it is
239   --  not specified whether empty regions are mutable or not, since there is
240   --  no byte no modify.
241
242   function Is_Mmapped (File : Mapped_File) return Boolean;
243   --  Whether regions for this file are opened through an mmap() system call
244   --  or equivalent. This is in general irrelevant to your application, unless
245   --  the file can be accessed by multiple concurrent processes or tasks. In
246   --  such a case, and if the file is indeed mmap-ed, then the various parts
247   --  of the file can be written simulatenously, and thus you cannot ensure
248   --  the integrity of the file. If the file is not mmapped, the latest
249   --  process to Close it overwrite what other processes have done.
250
251   function Get_Page_Size return Integer;
252   --  Returns the number of bytes in a page. Once a file is mapped from the
253   --  disk, its offset and Length should be multiples of this page size (which
254   --  is ensured by this package in any case). Knowing this page size allows
255   --  you to map as much memory as possible at once, thus potentially reducing
256   --  the number of system calls to read the file by chunks.
257
258   function Read_Whole_File
259     (Filename           : String;
260      Empty_If_Not_Found : Boolean := False)
261     return System.Strings.String_Access;
262   --  Returns the whole contents of the file.
263   --  The returned string must be freed by the user.
264   --  This is a convenience function, which is of course slower than the ones
265   --  above since we also need to allocate some memory, actually read the file
266   --  and copy the bytes.
267   --  If the file does not exist, null is returned. However, if
268   --  Empty_If_Not_Found is True, then the empty string is returned instead.
269   --  Filename should be compatible with the filesystem.
270
271private
272   pragma Inline (Data, Length, Last, Offset, Is_Mmapped, To_Str_Access);
273
274   type Mapped_File_Record;
275   type Mapped_File is access Mapped_File_Record;
276
277   type Mapped_Region_Record;
278   type Mapped_Region is access Mapped_Region_Record;
279
280   Invalid_Mapped_File   : constant Mapped_File := null;
281   Invalid_Mapped_Region : constant Mapped_Region := null;
282
283end System.Mmap;
284