1------------------------------------------------------------------------------ 2-- -- 3-- GNAT RUN-TIME COMPONENTS -- 4-- -- 5-- S Y S T E M . M M A P . O S _ I N T E R F A C E -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 2007-2020, 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 32with Ada.IO_Exceptions; 33with System.Strings; use System.Strings; 34 35with System.OS_Lib; 36pragma Unreferenced (System.OS_Lib); 37-- Only used to generate same runtime dependencies and same binder file on 38-- GNU/Linux and Windows. 39 40package body System.Mmap.OS_Interface is 41 42 use Win; 43 44 function Align 45 (Addr : File_Size) return File_Size; 46 -- Align some offset/length to the lowest page boundary 47 48 function Open_Common 49 (Filename : String; 50 Use_Mmap_If_Available : Boolean; 51 Write : Boolean) return System_File; 52 53 function From_UTF8 (Path : String) return Wide_String; 54 -- Convert from UTF-8 to Wide_String 55 56 --------------- 57 -- From_UTF8 -- 58 --------------- 59 60 function From_UTF8 (Path : String) return Wide_String is 61 function MultiByteToWideChar 62 (Codepage : Interfaces.C.unsigned; 63 Flags : Interfaces.C.unsigned; 64 Mbstr : Address; 65 Mb : Natural; 66 Wcstr : Address; 67 Wc : Natural) return Integer; 68 pragma Import (Stdcall, MultiByteToWideChar, "MultiByteToWideChar"); 69 70 Current_Codepage : Interfaces.C.unsigned; 71 pragma Import (C, Current_Codepage, "__gnat_current_codepage"); 72 73 Len : Natural; 74 begin 75 -- Compute length of the result 76 Len := MultiByteToWideChar 77 (Current_Codepage, 0, Path'Address, Path'Length, Null_Address, 0); 78 if Len = 0 then 79 raise Constraint_Error; 80 end if; 81 82 declare 83 -- Declare result 84 Res : Wide_String (1 .. Len); 85 begin 86 -- And compute it 87 Len := MultiByteToWideChar 88 (Current_Codepage, 0, 89 Path'Address, Path'Length, 90 Res'Address, Len); 91 if Len = 0 then 92 raise Constraint_Error; 93 end if; 94 return Res; 95 end; 96 end From_UTF8; 97 98 ----------------- 99 -- Open_Common -- 100 ----------------- 101 102 function Open_Common 103 (Filename : String; 104 Use_Mmap_If_Available : Boolean; 105 Write : Boolean) return System_File 106 is 107 dwDesiredAccess, dwShareMode : DWORD; 108 PageFlags : DWORD; 109 110 W_Filename : constant Wide_String := 111 From_UTF8 (Filename) & Wide_Character'Val (0); 112 File_Handle, Mapping_Handle : HANDLE; 113 114 SizeH : aliased DWORD; 115 Size : File_Size; 116 begin 117 if Write then 118 dwDesiredAccess := GENERIC_READ + GENERIC_WRITE; 119 dwShareMode := 0; 120 PageFlags := Win.PAGE_READWRITE; 121 else 122 dwDesiredAccess := GENERIC_READ; 123 dwShareMode := Win.FILE_SHARE_READ; 124 PageFlags := Win.PAGE_READONLY; 125 end if; 126 127 -- Actually open the file 128 129 File_Handle := CreateFile 130 (W_Filename'Address, dwDesiredAccess, dwShareMode, 131 null, OPEN_EXISTING, Win.FILE_ATTRIBUTE_NORMAL, 0); 132 133 if File_Handle = Win.INVALID_HANDLE_VALUE then 134 return Invalid_System_File; 135 end if; 136 137 -- Compute its size 138 139 Size := File_Size (Win.GetFileSize (File_Handle, SizeH'Access)); 140 141 if Size = Win.INVALID_FILE_SIZE then 142 return Invalid_System_File; 143 end if; 144 145 if SizeH /= 0 and then File_Size'Size > 32 then 146 Size := Size + (File_Size (SizeH) * 2 ** 32); 147 end if; 148 149 -- Then create a mapping object, if needed. On Win32, file memory 150 -- mapping is always available. 151 152 if Use_Mmap_If_Available then 153 Mapping_Handle := 154 Win.CreateFileMapping 155 (File_Handle, null, PageFlags, 156 0, DWORD (Size), Standard.System.Null_Address); 157 else 158 Mapping_Handle := Win.INVALID_HANDLE_VALUE; 159 end if; 160 161 return 162 (Handle => File_Handle, 163 Mapped => Use_Mmap_If_Available, 164 Mapping_Handle => Mapping_Handle, 165 Write => Write, 166 Length => Size); 167 end Open_Common; 168 169 --------------- 170 -- Open_Read -- 171 --------------- 172 173 function Open_Read 174 (Filename : String; 175 Use_Mmap_If_Available : Boolean := True) return System_File is 176 begin 177 return Open_Common (Filename, Use_Mmap_If_Available, False); 178 end Open_Read; 179 180 ---------------- 181 -- Open_Write -- 182 ---------------- 183 184 function Open_Write 185 (Filename : String; 186 Use_Mmap_If_Available : Boolean := True) return System_File is 187 begin 188 return Open_Common (Filename, Use_Mmap_If_Available, True); 189 end Open_Write; 190 191 ----------- 192 -- Close -- 193 ----------- 194 195 procedure Close (File : in out System_File) is 196 Ignored : BOOL; 197 pragma Unreferenced (Ignored); 198 begin 199 Ignored := CloseHandle (File.Mapping_Handle); 200 Ignored := CloseHandle (File.Handle); 201 File.Handle := Win.INVALID_HANDLE_VALUE; 202 File.Mapping_Handle := Win.INVALID_HANDLE_VALUE; 203 end Close; 204 205 -------------------- 206 -- Read_From_Disk -- 207 -------------------- 208 209 function Read_From_Disk 210 (File : System_File; 211 Offset, Length : File_Size) return System.Strings.String_Access 212 is 213 Buffer : String_Access := new String (1 .. Integer (Length)); 214 215 Pos : DWORD; 216 NbRead : aliased DWORD; 217 pragma Unreferenced (Pos); 218 begin 219 Pos := Win.SetFilePointer 220 (File.Handle, LONG (Offset), null, Win.FILE_BEGIN); 221 222 if Win.ReadFile 223 (File.Handle, Buffer.all'Address, 224 DWORD (Length), NbRead'Unchecked_Access, null) = Win.FALSE 225 then 226 System.Strings.Free (Buffer); 227 raise Ada.IO_Exceptions.Device_Error; 228 end if; 229 return Buffer; 230 end Read_From_Disk; 231 232 ------------------- 233 -- Write_To_Disk -- 234 ------------------- 235 236 procedure Write_To_Disk 237 (File : System_File; 238 Offset, Length : File_Size; 239 Buffer : System.Strings.String_Access) 240 is 241 Pos : DWORD; 242 NbWritten : aliased DWORD; 243 pragma Unreferenced (Pos); 244 begin 245 pragma Assert (File.Write); 246 Pos := Win.SetFilePointer 247 (File.Handle, LONG (Offset), null, Win.FILE_BEGIN); 248 249 if Win.WriteFile 250 (File.Handle, Buffer.all'Address, 251 DWORD (Length), NbWritten'Unchecked_Access, null) = Win.FALSE 252 then 253 raise Ada.IO_Exceptions.Device_Error; 254 end if; 255 end Write_To_Disk; 256 257 -------------------- 258 -- Create_Mapping -- 259 -------------------- 260 261 procedure Create_Mapping 262 (File : System_File; 263 Offset, Length : in out File_Size; 264 Mutable : Boolean; 265 Mapping : out System_Mapping) 266 is 267 Flags : DWORD; 268 begin 269 if File.Write then 270 Flags := Win.FILE_MAP_WRITE; 271 elsif Mutable then 272 Flags := Win.FILE_MAP_COPY; 273 else 274 Flags := Win.FILE_MAP_READ; 275 end if; 276 277 -- Adjust offset and mapping length to account for the required 278 -- alignment of offset on page boundary. 279 280 declare 281 Queried_Offset : constant File_Size := Offset; 282 begin 283 Offset := Align (Offset); 284 285 -- First extend the length to compensate the offset shift, then align 286 -- it on the upper page boundary, so that the whole queried area is 287 -- covered. 288 289 Length := Length + Queried_Offset - Offset; 290 Length := Align (Length + Get_Page_Size - 1); 291 292 -- But do not exceed the length of the file 293 if Offset + Length > File.Length then 294 Length := File.Length - Offset; 295 end if; 296 end; 297 298 if Length > File_Size (Integer'Last) then 299 raise Ada.IO_Exceptions.Device_Error; 300 else 301 Mapping := Invalid_System_Mapping; 302 Mapping.Address := 303 Win.MapViewOfFile 304 (File.Mapping_Handle, Flags, 305 0, DWORD (Offset), SIZE_T (Length)); 306 Mapping.Length := Length; 307 end if; 308 end Create_Mapping; 309 310 --------------------- 311 -- Dispose_Mapping -- 312 --------------------- 313 314 procedure Dispose_Mapping 315 (Mapping : in out System_Mapping) 316 is 317 Ignored : BOOL; 318 pragma Unreferenced (Ignored); 319 begin 320 Ignored := Win.UnmapViewOfFile (Mapping.Address); 321 Mapping := Invalid_System_Mapping; 322 end Dispose_Mapping; 323 324 ------------------- 325 -- Get_Page_Size -- 326 ------------------- 327 328 function Get_Page_Size return File_Size is 329 SystemInfo : aliased SYSTEM_INFO; 330 begin 331 GetSystemInfo (SystemInfo'Unchecked_Access); 332 return File_Size (SystemInfo.dwAllocationGranularity); 333 end Get_Page_Size; 334 335 ----------- 336 -- Align -- 337 ----------- 338 339 function Align 340 (Addr : File_Size) return File_Size is 341 begin 342 return Addr - Addr mod Get_Page_Size; 343 end Align; 344 345end System.Mmap.OS_Interface; 346