umapfile.cpp (11445B)
1 // © 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html 3 /* 4 ****************************************************************************** 5 * 6 * Copyright (C) 1999-2013, International Business Machines 7 * Corporation and others. All Rights Reserved. 8 * 9 ******************************************************************************/ 10 11 12 /*---------------------------------------------------------------------------- 13 * 14 * Memory mapped file wrappers for use by the ICU Data Implementation 15 * All of the platform-specific implementation for mapping data files 16 * is here. The rest of the ICU Data implementation uses only the 17 * wrapper functions. 18 * 19 *----------------------------------------------------------------------------*/ 20 /* Defines _XOPEN_SOURCE for access to POSIX functions. 21 * Must be before any other #includes. */ 22 #include "uposixdefs.h" 23 24 #include "unicode/putil.h" 25 #include "unicode/ustring.h" 26 #include "udatamem.h" 27 #include "umapfile.h" 28 29 /* memory-mapping base definitions ------------------------------------------ */ 30 31 #if MAP_IMPLEMENTATION==MAP_WIN32 32 #ifndef WIN32_LEAN_AND_MEAN 33 # define WIN32_LEAN_AND_MEAN 34 #endif 35 # define VC_EXTRALEAN 36 # define NOUSER 37 # define NOSERVICE 38 # define NOIME 39 # define NOMCX 40 41 # if U_PLATFORM_HAS_WINUWP_API == 1 42 // Some previous versions of the Windows 10 SDK don't expose various APIs for UWP applications 43 // to use, even though UWP apps are allowed to call and use them. Temporarily change the 44 // WINAPI family partition below to Desktop, so that function declarations are visible for UWP. 45 # include <winapifamily.h> 46 # if !(WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)) 47 # pragma push_macro("WINAPI_PARTITION_DESKTOP") 48 # undef WINAPI_PARTITION_DESKTOP 49 # define WINAPI_PARTITION_DESKTOP 1 50 # define CHANGED_WINAPI_PARTITION_DESKTOP_VALUE 51 # endif 52 # endif 53 54 # include <windows.h> 55 56 # if U_PLATFORM_HAS_WINUWP_API == 1 && defined(CHANGED_WINAPI_PARTITION_DESKTOP_VALUE) 57 # pragma pop_macro("WINAPI_PARTITION_DESKTOP") 58 # endif 59 60 # include "cmemory.h" 61 62 typedef HANDLE MemoryMap; 63 64 # define IS_MAP(map) ((map)!=nullptr) 65 66 #elif MAP_IMPLEMENTATION==MAP_POSIX 67 typedef size_t MemoryMap; 68 69 # define IS_MAP(map) ((map)!=0) 70 71 # include <unistd.h> 72 # include <sys/mman.h> 73 # include <sys/stat.h> 74 # include <fcntl.h> 75 76 # ifndef MAP_FAILED 77 # define MAP_FAILED ((void*)-1) 78 # endif 79 #elif MAP_IMPLEMENTATION==MAP_STDIO 80 # include <stdio.h> 81 # include "cmemory.h" 82 83 typedef void *MemoryMap; 84 85 # define IS_MAP(map) ((map)!=nullptr) 86 #endif 87 88 /*----------------------------------------------------------------------------* 89 * * 90 * Memory Mapped File support. Platform dependent implementation of * 91 * functions used by the rest of the implementation.* 92 * * 93 *----------------------------------------------------------------------------*/ 94 #if MAP_IMPLEMENTATION==MAP_NONE 95 U_CFUNC UBool 96 uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { 97 if (U_FAILURE(*status)) { 98 return false; 99 } 100 UDataMemory_init(pData); /* Clear the output struct. */ 101 return false; /* no file access */ 102 } 103 104 U_CFUNC void uprv_unmapFile(UDataMemory *pData) { 105 /* nothing to do */ 106 } 107 #elif MAP_IMPLEMENTATION==MAP_WIN32 108 U_CFUNC UBool 109 uprv_mapFile( 110 UDataMemory *pData, /* Fill in with info on the result doing the mapping. */ 111 /* Output only; any original contents are cleared. */ 112 const char *path, /* File path to be opened/mapped. */ 113 UErrorCode *status /* Error status, used to report out-of-memory errors. */ 114 ) 115 { 116 if (U_FAILURE(*status)) { 117 return false; 118 } 119 120 HANDLE map = nullptr; 121 HANDLE file = INVALID_HANDLE_VALUE; 122 DWORD fileLength = 0; 123 124 UDataMemory_init(pData); /* Clear the output struct. */ 125 126 /* open the input file */ 127 #if U_PLATFORM_HAS_WINUWP_API == 0 128 // Note: In the non-UWP code-path (ie: Win32), the value of the path variable might have come from 129 // the CRT 'getenv' function, and would be therefore be encoded in the default ANSI code page. 130 // This means that we can't call the *W version of API below, whereas in the UWP code-path 131 // there is no 'getenv' call, and thus the string will be only UTF-8/Invariant characters. 132 file=CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, nullptr, 133 OPEN_EXISTING, 134 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, nullptr); 135 #else 136 // Convert from UTF-8 string to UTF-16 string. 137 wchar_t utf16Path[MAX_PATH]; 138 int32_t pathUtf16Len = 0; 139 u_strFromUTF8(reinterpret_cast<char16_t*>(utf16Path), static_cast<int32_t>(UPRV_LENGTHOF(utf16Path)), &pathUtf16Len, path, -1, status); 140 141 if (U_FAILURE(*status)) { 142 return false; 143 } 144 if (*status == U_STRING_NOT_TERMINATED_WARNING) { 145 // Report back an error instead of a warning. 146 *status = U_BUFFER_OVERFLOW_ERROR; 147 return false; 148 } 149 150 file = CreateFileW(utf16Path, GENERIC_READ, FILE_SHARE_READ, nullptr, 151 OPEN_EXISTING, 152 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, nullptr); 153 #endif 154 if (file == INVALID_HANDLE_VALUE) { 155 // If we failed to open the file due to an out-of-memory error, then we want 156 // to report that error back to the caller. 157 if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) { 158 *status = U_MEMORY_ALLOCATION_ERROR; 159 } 160 return false; 161 } 162 163 fileLength = GetFileSize(file, nullptr); 164 165 // Note: We use nullptr/nullptr for lpAttributes parameter below. 166 // This means our handle cannot be inherited and we will get the default security descriptor. 167 /* create an unnamed Windows file-mapping object for the specified file */ 168 map = CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0, nullptr); 169 170 CloseHandle(file); 171 if (map == nullptr) { 172 // If we failed to create the mapping due to an out-of-memory error, then 173 // we want to report that error back to the caller. 174 if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) { 175 *status = U_MEMORY_ALLOCATION_ERROR; 176 } 177 return false; 178 } 179 180 /* map a view of the file into our address space */ 181 pData->pHeader = reinterpret_cast<const DataHeader *>(MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0)); 182 if (pData->pHeader == nullptr) { 183 CloseHandle(map); 184 return false; 185 } 186 pData->map = map; 187 pData->length = fileLength; 188 189 return true; 190 } 191 192 U_CFUNC void 193 uprv_unmapFile(UDataMemory *pData) { 194 if (pData != nullptr && pData->map != nullptr) { 195 UnmapViewOfFile(pData->pHeader); 196 CloseHandle(pData->map); 197 pData->pHeader = nullptr; 198 pData->map = nullptr; 199 } 200 } 201 202 203 204 #elif MAP_IMPLEMENTATION==MAP_POSIX 205 U_CFUNC UBool 206 uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { 207 int fd; 208 int length; 209 struct stat mystat; 210 void *data; 211 212 if (U_FAILURE(*status)) { 213 return false; 214 } 215 216 UDataMemory_init(pData); /* Clear the output struct. */ 217 218 /* determine the length of the file */ 219 if(stat(path, &mystat)!=0 || mystat.st_size<=0) { 220 return false; 221 } 222 length=mystat.st_size; 223 224 /* open the file */ 225 fd=open(path, O_RDONLY); 226 if(fd==-1) { 227 return false; 228 } 229 230 /* get a view of the mapping */ 231 #if U_PLATFORM != U_PF_HPUX 232 data=mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, 0); 233 #else 234 data=mmap(nullptr, length, PROT_READ, MAP_PRIVATE, fd, 0); 235 #endif 236 close(fd); /* no longer needed */ 237 if(data==MAP_FAILED) { 238 // Possibly check the errno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR? 239 return false; 240 } 241 242 pData->map = (char *)data + length; 243 pData->pHeader=(const DataHeader *)data; 244 pData->mapAddr = data; 245 pData->length = length; 246 #if U_PLATFORM == U_PF_IPHONE || U_PLATFORM == U_PF_ANDROID 247 // Apparently supported from Android 23 and higher: 248 // https://github.com/ggml-org/llama.cpp/pull/3631 249 // Checking for the flag itself is safer than checking for __ANDROID_API__. 250 # ifdef POSIX_MADV_RANDOM 251 posix_madvise(data, length, POSIX_MADV_RANDOM); 252 # endif 253 #endif 254 return true; 255 } 256 257 U_CFUNC void 258 uprv_unmapFile(UDataMemory *pData) { 259 if(pData!=nullptr && pData->map!=nullptr) { 260 size_t dataLen = (char *)pData->map - (char *)pData->mapAddr; 261 if(munmap(pData->mapAddr, dataLen)==-1) { 262 } 263 pData->pHeader=nullptr; 264 pData->map=nullptr; 265 pData->mapAddr=nullptr; 266 } 267 } 268 269 270 271 #elif MAP_IMPLEMENTATION==MAP_STDIO 272 /* copy of the filestrm.c/T_FileStream_size() implementation */ 273 static int32_t 274 umap_fsize(FILE *f) { 275 int32_t savedPos = ftell(f); 276 int32_t size = 0; 277 278 /*Changes by Bertrand A. D. doesn't affect the current position 279 goes to the end of the file before ftell*/ 280 fseek(f, 0, SEEK_END); 281 size = (int32_t)ftell(f); 282 fseek(f, savedPos, SEEK_SET); 283 return size; 284 } 285 286 U_CFUNC UBool 287 uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) { 288 FILE *file; 289 int32_t fileLength; 290 void *p; 291 292 if (U_FAILURE(*status)) { 293 return false; 294 } 295 296 UDataMemory_init(pData); /* Clear the output struct. */ 297 /* open the input file */ 298 file=fopen(path, "rb"); 299 if(file==nullptr) { 300 return false; 301 } 302 303 /* get the file length */ 304 fileLength=umap_fsize(file); 305 if(ferror(file) || fileLength<=20) { 306 fclose(file); 307 return false; 308 } 309 310 /* allocate the memory to hold the file data */ 311 p=uprv_malloc(fileLength); 312 if(p==nullptr) { 313 fclose(file); 314 *status = U_MEMORY_ALLOCATION_ERROR; 315 return false; 316 } 317 318 /* read the file */ 319 if(fileLength!=fread(p, 1, fileLength, file)) { 320 uprv_free(p); 321 fclose(file); 322 return false; 323 } 324 325 fclose(file); 326 pData->map=p; 327 pData->pHeader=(const DataHeader *)p; 328 pData->mapAddr=p; 329 pData->length = fileLength; 330 return true; 331 } 332 333 U_CFUNC void 334 uprv_unmapFile(UDataMemory *pData) { 335 if(pData!=nullptr && pData->map!=nullptr) { 336 uprv_free(pData->map); 337 pData->map = nullptr; 338 pData->mapAddr = nullptr; 339 pData->pHeader = nullptr; 340 } 341 } 342 #else 343 # error MAP_IMPLEMENTATION is set incorrectly 344 #endif