aboutsummaryrefslogtreecommitdiff
path: root/gcc/ada/s-mmap.adb
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/ada/s-mmap.adb')
-rw-r--r--gcc/ada/s-mmap.adb548
1 files changed, 548 insertions, 0 deletions
diff --git a/gcc/ada/s-mmap.adb b/gcc/ada/s-mmap.adb
new file mode 100644
index 0000000..e9b2aff
--- /dev/null
+++ b/gcc/ada/s-mmap.adb
@@ -0,0 +1,548 @@
+------------------------------------------------------------------------------
+-- --
+-- GNAT RUN-TIME COMPONENTS --
+-- --
+-- S Y S T E M . M M A P --
+-- --
+-- B o d y --
+-- --
+-- Copyright (C) 2007-2016, AdaCore --
+-- --
+-- This library is free software; you can redistribute it and/or modify it --
+-- under terms of the GNU General Public License as published by the Free --
+-- Software Foundation; either version 3, or (at your option) any later --
+-- version. This library is distributed in the hope that it will be useful, --
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- --
+-- TABILITY or FITNESS FOR A PARTICULAR PURPOSE. --
+-- --
+-- As a special exception under Section 7 of GPL version 3, you are granted --
+-- additional permissions described in the GCC Runtime Library Exception, --
+-- version 3.1, as published by the Free Software Foundation. --
+-- --
+-- You should have received a copy of the GNU General Public License and --
+-- a copy of the GCC Runtime Library Exception along with this program; --
+-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
+-- <http://www.gnu.org/licenses/>. --
+-- --
+-- GNAT was originally developed by the GNAT team at New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc. --
+-- --
+------------------------------------------------------------------------------
+
+with Ada.IO_Exceptions;
+with Ada.Unchecked_Conversion;
+with Ada.Unchecked_Deallocation;
+
+with System.Strings; use System.Strings;
+
+with System.Mmap.OS_Interface; use System.Mmap.OS_Interface;
+
+package body System.Mmap is
+
+ type Mapped_File_Record is record
+ Current_Region : Mapped_Region;
+ -- The legacy API enables only one region to be mapped, directly
+ -- associated with the mapped file. This references this region.
+
+ File : System_File;
+ -- Underlying OS-level file
+ end record;
+
+ type Mapped_Region_Record is record
+ File : Mapped_File;
+ -- The file this region comes from. Be careful: for reading file, it is
+ -- valid to have it closed before one of its regions is free'd.
+
+ Write : Boolean;
+ -- Whether the file this region comes from is open for writing.
+
+ Data : Str_Access;
+ -- Unbounded access to the mapped content.
+
+ System_Offset : File_Size;
+ -- Position in the file of the first byte actually mapped in memory
+
+ User_Offset : File_Size;
+ -- Position in the file of the first byte requested by the user
+
+ System_Size : File_Size;
+ -- Size of the region actually mapped in memory
+
+ User_Size : File_Size;
+ -- Size of the region requested by the user
+
+ Mapped : Boolean;
+ -- Whether this region is actually memory mapped
+
+ Mutable : Boolean;
+ -- If the file is opened for reading, wheter this region is writable
+
+ Buffer : System.Strings.String_Access;
+ -- When this region is not actually memory mapped, contains the
+ -- requested bytes.
+
+ Mapping : System_Mapping;
+ -- Underlying OS-level data for the mapping, if any
+ end record;
+
+ Invalid_Mapped_Region_Record : constant Mapped_Region_Record :=
+ (null, False, null, 0, 0, 0, 0, False, False, null,
+ Invalid_System_Mapping);
+ Invalid_Mapped_File_Record : constant Mapped_File_Record :=
+ (Invalid_Mapped_Region, Invalid_System_File);
+
+ Empty_String : constant String := "";
+ -- Used to provide a valid empty Data for empty files, for instanc.
+
+ procedure Dispose is new Ada.Unchecked_Deallocation
+ (Mapped_File_Record, Mapped_File);
+ procedure Dispose is new Ada.Unchecked_Deallocation
+ (Mapped_Region_Record, Mapped_Region);
+
+ function Convert is new Ada.Unchecked_Conversion
+ (Standard.System.Address, Str_Access);
+
+ procedure Compute_Data (Region : Mapped_Region);
+ -- Fill the Data field according to system and user offsets. The region
+ -- must actually be mapped or bufferized.
+
+ procedure From_Disk (Region : Mapped_Region);
+ -- Read a region of some file from the disk
+
+ procedure To_Disk (Region : Mapped_Region);
+ -- Write the region of the file back to disk if necessary, and free memory
+
+ ---------------
+ -- Open_Read --
+ ---------------
+
+ function Open_Read
+ (Filename : String;
+ Use_Mmap_If_Available : Boolean := True) return Mapped_File
+ is
+ File : constant System_File :=
+ Open_Read (Filename, Use_Mmap_If_Available);
+ begin
+ return new Mapped_File_Record'
+ (Current_Region => Invalid_Mapped_Region,
+ File => File);
+ end Open_Read;
+
+ ----------------
+ -- Open_Write --
+ ----------------
+
+ function Open_Write
+ (Filename : String;
+ Use_Mmap_If_Available : Boolean := True) return Mapped_File
+ is
+ File : constant System_File :=
+ Open_Write (Filename, Use_Mmap_If_Available);
+ begin
+ return new Mapped_File_Record'
+ (Current_Region => Invalid_Mapped_Region,
+ File => File);
+ end Open_Write;
+
+ -----------
+ -- Close --
+ -----------
+
+ procedure Close (File : in out Mapped_File) is
+ begin
+ -- Closing a closed file is allowed and should do nothing
+
+ if File = Invalid_Mapped_File then
+ return;
+ end if;
+
+ if File.Current_Region /= null then
+ Free (File.Current_Region);
+ end if;
+
+ if File.File /= Invalid_System_File then
+ Close (File.File);
+ end if;
+
+ Dispose (File);
+ end Close;
+
+ ----------
+ -- Free --
+ ----------
+
+ procedure Free (Region : in out Mapped_Region) is
+ Ignored : Integer;
+ pragma Unreferenced (Ignored);
+ begin
+ -- Freeing an already free'd file is allowed and should do nothing
+
+ if Region = Invalid_Mapped_Region then
+ return;
+ end if;
+
+ if Region.Mapping /= Invalid_System_Mapping then
+ Dispose_Mapping (Region.Mapping);
+ end if;
+ To_Disk (Region);
+ Dispose (Region);
+ end Free;
+
+ ----------
+ -- Read --
+ ----------
+
+ procedure Read
+ (File : Mapped_File;
+ Region : in out Mapped_Region;
+ Offset : File_Size := 0;
+ Length : File_Size := 0;
+ Mutable : Boolean := False)
+ is
+ File_Length : constant File_Size := Mmap.Length (File);
+
+ Req_Offset : constant File_Size := Offset;
+ Req_Length : File_Size := Length;
+ -- Offset and Length of the region to map, used to adjust mapping
+ -- bounds, reflecting what the user will see.
+
+ Region_Allocated : Boolean := False;
+ begin
+ -- If this region comes from another file, or simply if the file is
+ -- writeable, we cannot re-use this mapping: free it first.
+
+ if Region /= Invalid_Mapped_Region
+ and then
+ (Region.File /= File or else File.File.Write)
+ then
+ Free (Region);
+ end if;
+
+ if Region = Invalid_Mapped_Region then
+ Region := new Mapped_Region_Record'(Invalid_Mapped_Region_Record);
+ Region_Allocated := True;
+ end if;
+
+ Region.File := File;
+
+ if Req_Offset >= File_Length then
+ -- If the requested offset goes beyond file size, map nothing
+
+ Req_Length := 0;
+
+ elsif Length = 0
+ or else
+ Length > File_Length - Req_Offset
+ then
+ -- If Length is 0 or goes beyond file size, map till end of file
+
+ Req_Length := File_Length - Req_Offset;
+
+ else
+ Req_Length := Length;
+ end if;
+
+ -- Past this point, the offset/length the user will see is fixed. On the
+ -- other hand, the system offset/length is either already defined, from
+ -- a previous mapping, or it is set to 0. In the latter case, the next
+ -- step will set them according to the mapping.
+
+ Region.User_Offset := Req_Offset;
+ Region.User_Size := Req_Length;
+
+ -- If the requested region is inside an already mapped region, adjust
+ -- user-requested data and do nothing else.
+
+ if (File.File.Write or else Region.Mutable = Mutable)
+ and then
+ Req_Offset >= Region.System_Offset
+ and then
+ (Req_Offset + Req_Length
+ <= Region.System_Offset + Region.System_Size)
+ then
+ Region.User_Offset := Req_Offset;
+ Compute_Data (Region);
+ return;
+
+ elsif Region.Buffer /= null then
+ -- Otherwise, as we are not going to re-use the buffer, free it
+
+ System.Strings.Free (Region.Buffer);
+ Region.Buffer := null;
+
+ elsif Region.Mapping /= Invalid_System_Mapping then
+ -- Otherwise, there is a memory mapping that we need to unmap.
+ Dispose_Mapping (Region.Mapping);
+ end if;
+
+ -- mmap() will sometimes return NULL when the file exists but is empty,
+ -- which is not what we want, so in the case of a zero length file we
+ -- fall back to read(2)/write(2)-based mode.
+
+ if File_Length > 0 and then File.File.Mapped then
+
+ Region.System_Offset := Req_Offset;
+ Region.System_Size := Req_Length;
+ Create_Mapping
+ (File.File,
+ Region.System_Offset, Region.System_Size,
+ Mutable,
+ Region.Mapping);
+ Region.Mapped := True;
+ Region.Mutable := Mutable;
+
+ else
+ -- There is no alignment requirement when manually reading the file.
+
+ Region.System_Offset := Req_Offset;
+ Region.System_Size := Req_Length;
+ Region.Mapped := False;
+ Region.Mutable := True;
+ From_Disk (Region);
+ end if;
+
+ Region.Write := File.File.Write;
+ Compute_Data (Region);
+
+ exception
+ when others =>
+ -- Before propagating any exception, free any region we allocated
+ -- here.
+
+ if Region_Allocated then
+ Dispose (Region);
+ end if;
+ raise;
+ end Read;
+
+ ----------
+ -- Read --
+ ----------
+
+ procedure Read
+ (File : Mapped_File;
+ Offset : File_Size := 0;
+ Length : File_Size := 0;
+ Mutable : Boolean := False)
+ is
+ begin
+ Read (File, File.Current_Region, Offset, Length, Mutable);
+ end Read;
+
+ ----------
+ -- Read --
+ ----------
+
+ function Read
+ (File : Mapped_File;
+ Offset : File_Size := 0;
+ Length : File_Size := 0;
+ Mutable : Boolean := False) return Mapped_Region
+ is
+ Region : Mapped_Region := Invalid_Mapped_Region;
+ begin
+ Read (File, Region, Offset, Length, Mutable);
+ return Region;
+ end Read;
+
+ ------------
+ -- Length --
+ ------------
+
+ function Length (File : Mapped_File) return File_Size is
+ begin
+ return File.File.Length;
+ end Length;
+
+ ------------
+ -- Offset --
+ ------------
+
+ function Offset (Region : Mapped_Region) return File_Size is
+ begin
+ return Region.User_Offset;
+ end Offset;
+
+ ------------
+ -- Offset --
+ ------------
+
+ function Offset (File : Mapped_File) return File_Size is
+ begin
+ return Offset (File.Current_Region);
+ end Offset;
+
+ ----------
+ -- Last --
+ ----------
+
+ function Last (Region : Mapped_Region) return Integer is
+ begin
+ return Integer (Region.User_Size);
+ end Last;
+
+ ----------
+ -- Last --
+ ----------
+
+ function Last (File : Mapped_File) return Integer is
+ begin
+ return Last (File.Current_Region);
+ end Last;
+
+ -------------------
+ -- To_Str_Access --
+ -------------------
+
+ function To_Str_Access
+ (Str : System.Strings.String_Access) return Str_Access is
+ begin
+ if Str = null then
+ return null;
+ else
+ return Convert (Str.all'Address);
+ end if;
+ end To_Str_Access;
+
+ ----------
+ -- Data --
+ ----------
+
+ function Data (Region : Mapped_Region) return Str_Access is
+ begin
+ return Region.Data;
+ end Data;
+
+ ----------
+ -- Data --
+ ----------
+
+ function Data (File : Mapped_File) return Str_Access is
+ begin
+ return Data (File.Current_Region);
+ end Data;
+
+ ----------------
+ -- Is_Mutable --
+ ----------------
+
+ function Is_Mutable (Region : Mapped_Region) return Boolean is
+ begin
+ return Region.Mutable or Region.Write;
+ end Is_Mutable;
+
+ ----------------
+ -- Is_Mmapped --
+ ----------------
+
+ function Is_Mmapped (File : Mapped_File) return Boolean is
+ begin
+ return File.File.Mapped;
+ end Is_Mmapped;
+
+ -------------------
+ -- Get_Page_Size --
+ -------------------
+
+ function Get_Page_Size return Integer is
+ Result : constant File_Size := Get_Page_Size;
+ begin
+ return Integer (Result);
+ end Get_Page_Size;
+
+ ---------------------
+ -- Read_Whole_File --
+ ---------------------
+
+ function Read_Whole_File
+ (Filename : String;
+ Empty_If_Not_Found : Boolean := False)
+ return System.Strings.String_Access
+ is
+ File : Mapped_File := Open_Read (Filename);
+ Region : Mapped_Region renames File.Current_Region;
+ Result : String_Access;
+ begin
+ Read (File);
+
+ if Region.Data /= null then
+ Result := new String'(String
+ (Region.Data (1 .. Last (Region))));
+
+ elsif Region.Buffer /= null then
+ Result := Region.Buffer;
+ Region.Buffer := null; -- So that it is not deallocated
+ end if;
+
+ Close (File);
+
+ return Result;
+
+ exception
+ when Ada.IO_Exceptions.Name_Error =>
+ if Empty_If_Not_Found then
+ return new String'("");
+ else
+ return null;
+ end if;
+
+ when others =>
+ Close (File);
+ return null;
+ end Read_Whole_File;
+
+ ---------------
+ -- From_Disk --
+ ---------------
+
+ procedure From_Disk (Region : Mapped_Region) is
+ begin
+ pragma Assert (Region.File.all /= Invalid_Mapped_File_Record);
+ pragma Assert (Region.Buffer = null);
+
+ Region.Buffer := Read_From_Disk
+ (Region.File.File, Region.User_Offset, Region.User_Size);
+ Region.Mapped := False;
+ end From_Disk;
+
+ -------------
+ -- To_Disk --
+ -------------
+
+ procedure To_Disk (Region : Mapped_Region) is
+ begin
+ if Region.Write and then Region.Buffer /= null then
+ pragma Assert (Region.File.all /= Invalid_Mapped_File_Record);
+ Write_To_Disk
+ (Region.File.File,
+ Region.User_Offset, Region.User_Size,
+ Region.Buffer);
+ end if;
+
+ System.Strings.Free (Region.Buffer);
+ Region.Buffer := null;
+ end To_Disk;
+
+ ------------------
+ -- Compute_Data --
+ ------------------
+
+ procedure Compute_Data (Region : Mapped_Region) is
+ Base_Data : Str_Access;
+ -- Address of the first byte actually mapped in memory
+
+ Data_Shift : constant Integer :=
+ Integer (Region.User_Offset - Region.System_Offset);
+ begin
+ if Region.User_Size = 0 then
+ Region.Data := Convert (Empty_String'Address);
+ return;
+ elsif Region.Mapped then
+ Base_Data := Convert (Region.Mapping.Address);
+ else
+ Base_Data := Convert (Region.Buffer.all'Address);
+ end if;
+ Region.Data := Convert (Base_Data (Data_Shift + 1)'Address);
+ end Compute_Data;
+
+end System.Mmap;