1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 ******************************************************************************
6 * Copyright (C) 1999-2013, International Business Machines
7 * Corporation and others. All Rights Reserved.
9 ******************************************************************************/
12 /*----------------------------------------------------------------------------
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
19 *----------------------------------------------------------------------------*/
20 /* Defines _XOPEN_SOURCE for access to POSIX functions.
21 * Must be before any other #includes. */
22 #include "uposixdefs.h"
24 #include "unicode/putil.h"
28 /* memory-mapping base definitions ------------------------------------------ */
30 #if MAP_IMPLEMENTATION==MAP_WIN32
31 #ifndef WIN32_LEAN_AND_MEAN
32 # define WIN32_LEAN_AND_MEAN
42 typedef HANDLE MemoryMap
;
44 # define IS_MAP(map) ((map)!=NULL)
45 #elif MAP_IMPLEMENTATION==MAP_POSIX || MAP_IMPLEMENTATION==MAP_390DLL
46 typedef size_t MemoryMap
;
48 # define IS_MAP(map) ((map)!=0)
51 # include <sys/mman.h>
52 # include <sys/stat.h>
56 # define MAP_FAILED ((void*)-1)
59 # if MAP_IMPLEMENTATION==MAP_390DLL
60 /* No memory mapping for 390 batch mode. Fake it using dll loading. */
64 # include "unicode/udata.h"
65 # define LIB_PREFIX "lib"
66 # define LIB_SUFFIX ".dll"
67 /* This is inconvienient until we figure out what to do with U_ICUDATA_NAME in utypes.h */
68 # define U_ICUDATA_ENTRY_NAME "icudt" U_ICU_VERSION_SHORT U_LIB_SUFFIX_C_NAME_STRING "_dat"
70 #elif MAP_IMPLEMENTATION==MAP_STDIO
74 typedef void *MemoryMap
;
76 # define IS_MAP(map) ((map)!=NULL)
79 /*----------------------------------------------------------------------------*
81 * Memory Mapped File support. Platform dependent implementation of *
82 * functions used by the rest of the implementation.*
84 *----------------------------------------------------------------------------*/
85 #if MAP_IMPLEMENTATION==MAP_NONE
87 uprv_mapFile(UDataMemory
*pData
, const char *path
) {
88 UDataMemory_init(pData
); /* Clear the output struct. */
89 return FALSE
; /* no file access */
92 U_CFUNC
void uprv_unmapFile(UDataMemory
*pData
) {
95 #elif MAP_IMPLEMENTATION==MAP_WIN32
98 UDataMemory
*pData
, /* Fill in with info on the result doing the mapping. */
99 /* Output only; any original contents are cleared. */
100 const char *path
/* File path to be opened/mapped */
105 SECURITY_ATTRIBUTES mappingAttributes
;
106 SECURITY_ATTRIBUTES
*mappingAttributesPtr
= NULL
;
107 SECURITY_DESCRIPTOR securityDesc
;
109 UDataMemory_init(pData
); /* Clear the output struct. */
111 /* open the input file */
112 #if U_PLATFORM_HAS_WINUWP_API == 0
113 file
=CreateFileA(path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
115 FILE_ATTRIBUTE_NORMAL
|FILE_FLAG_RANDOM_ACCESS
, NULL
);
117 // First we need to go from char to UTF-16
118 // u_UCharsToChars could work but it requires length.
119 WCHAR utf16Path
[MAX_PATH
];
121 for (i
= 0; i
< UPRV_LENGTHOF(utf16Path
); i
++)
123 utf16Path
[i
] = path
[i
];
129 if (i
>= UPRV_LENGTHOF(utf16Path
))
131 // Ran out of room, unlikely but be safe
132 utf16Path
[UPRV_LENGTHOF(utf16Path
) - 1] = '\0';
135 // TODO: Is it worth setting extended parameters to specify random access?
136 file
= CreateFile2(utf16Path
, GENERIC_READ
, FILE_SHARE_READ
, OPEN_EXISTING
, NULL
);
138 if(file
==INVALID_HANDLE_VALUE
) {
142 /* Declare and initialize a security descriptor.
143 This is required for multiuser systems on Windows 2000 SP4 and beyond */
144 // TODO: UWP does not have this function and I do not think it is required?
145 #if U_PLATFORM_HAS_WINUWP_API == 0
146 if (InitializeSecurityDescriptor(&securityDesc
, SECURITY_DESCRIPTOR_REVISION
)) {
147 /* give the security descriptor a Null Dacl done using the "TRUE, (PACL)NULL" here */
148 if (SetSecurityDescriptorDacl(&securityDesc
, TRUE
, (PACL
)NULL
, FALSE
)) {
149 /* Make the security attributes point to the security descriptor */
150 uprv_memset(&mappingAttributes
, 0, sizeof(mappingAttributes
));
151 mappingAttributes
.nLength
= sizeof(mappingAttributes
);
152 mappingAttributes
.lpSecurityDescriptor
= &securityDesc
;
153 mappingAttributes
.bInheritHandle
= FALSE
; /* object uninheritable */
154 mappingAttributesPtr
= &mappingAttributes
;
157 /* else creating security descriptors can fail when we are on Windows 98,
158 and mappingAttributesPtr == NULL for that case. */
160 /* create an unnamed Windows file-mapping object for the specified file */
161 map
=CreateFileMapping(file
, mappingAttributesPtr
, PAGE_READONLY
, 0, 0, NULL
);
163 map
= CreateFileMappingFromApp(file
, NULL
, PAGE_READONLY
, 0, NULL
);
170 /* map a view of the file into our address space */
171 pData
->pHeader
=(const DataHeader
*)MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
172 if(pData
->pHeader
==NULL
) {
181 uprv_unmapFile(UDataMemory
*pData
) {
182 if(pData
!=NULL
&& pData
->map
!=NULL
) {
183 UnmapViewOfFile(pData
->pHeader
);
184 CloseHandle(pData
->map
);
192 #elif MAP_IMPLEMENTATION==MAP_POSIX
194 uprv_mapFile(UDataMemory
*pData
, const char *path
) {
200 UDataMemory_init(pData
); /* Clear the output struct. */
202 /* determine the length of the file */
203 if(stat(path
, &mystat
)!=0 || mystat
.st_size
<=0) {
206 length
=mystat
.st_size
;
209 fd
=open(path
, O_RDONLY
);
214 /* get a view of the mapping */
215 #if U_PLATFORM != U_PF_HPUX
216 data
=mmap(0, length
, PROT_READ
, MAP_SHARED
, fd
, 0);
218 data
=mmap(0, length
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
220 close(fd
); /* no longer needed */
221 if(data
==MAP_FAILED
) {
225 pData
->map
= (char *)data
+ length
;
226 pData
->pHeader
=(const DataHeader
*)data
;
227 pData
->mapAddr
= data
;
228 #if U_PLATFORM == U_PF_IPHONE
229 posix_madvise(data
, length
, POSIX_MADV_RANDOM
);
235 uprv_unmapFile(UDataMemory
*pData
) {
236 if(pData
!=NULL
&& pData
->map
!=NULL
) {
237 size_t dataLen
= (char *)pData
->map
- (char *)pData
->mapAddr
;
238 if(munmap(pData
->mapAddr
, dataLen
)==-1) {
248 #elif MAP_IMPLEMENTATION==MAP_STDIO
249 /* copy of the filestrm.c/T_FileStream_size() implementation */
251 umap_fsize(FILE *f
) {
252 int32_t savedPos
= ftell(f
);
255 /*Changes by Bertrand A. D. doesn't affect the current position
256 goes to the end of the file before ftell*/
257 fseek(f
, 0, SEEK_END
);
258 size
= (int32_t)ftell(f
);
259 fseek(f
, savedPos
, SEEK_SET
);
264 uprv_mapFile(UDataMemory
*pData
, const char *path
) {
269 UDataMemory_init(pData
); /* Clear the output struct. */
270 /* open the input file */
271 file
=fopen(path
, "rb");
276 /* get the file length */
277 fileLength
=umap_fsize(file
);
278 if(ferror(file
) || fileLength
<=20) {
283 /* allocate the memory to hold the file data */
284 p
=uprv_malloc(fileLength
);
291 if(fileLength
!=fread(p
, 1, fileLength
, file
)) {
299 pData
->pHeader
=(const DataHeader
*)p
;
305 uprv_unmapFile(UDataMemory
*pData
) {
306 if(pData
!=NULL
&& pData
->map
!=NULL
) {
307 uprv_free(pData
->map
);
309 pData
->mapAddr
= NULL
;
310 pData
->pHeader
= NULL
;
315 #elif MAP_IMPLEMENTATION==MAP_390DLL
316 /* 390 specific Library Loading.
317 * This is the only platform left that dynamically loads an ICU Data Library.
318 * All other platforms use .data files when dynamic loading is required, but
319 * this turn out to be awkward to support in 390 batch mode.
321 * The idea here is to hide the fact that 390 is using dll loading from the
322 * rest of ICU, and make it look like there is file loading happening.
326 static char *strcpy_returnEnd(char *dest
, const char *src
)
328 while((*dest
=*src
)!=0) {
335 /*------------------------------------------------------------------------------
337 * computeDirPath given a user-supplied path of an item to be opened,
339 * - the full directory path to be used
340 * when opening the file.
341 * - Pointer to null at end of above returned path
344 * path: input path. Buffer is not altered.
345 * pathBuffer: Output buffer. Any contents are overwritten.
348 * Pointer to null termination in returned pathBuffer.
350 * TODO: This works the way ICU historically has, but the
351 * whole data fallback search path is so complicated that
352 * proabably almost no one will ever really understand it,
353 * the potential for confusion is large. (It's not just
354 * this one function, but the whole scheme.)
356 *------------------------------------------------------------------------------*/
357 static char *uprv_computeDirPath(const char *path
, char *pathBuffer
)
359 char *finalSlash
; /* Ptr to last dir separator in input path, or null if none. */
360 int32_t pathLen
; /* Length of the returned directory path */
364 finalSlash
= uprv_strrchr(path
, U_FILE_SEP_CHAR
);
368 if (finalSlash
== 0) {
369 /* No user-supplied path.
370 * Copy the ICU_DATA path to the path buffer and return that*/
371 const char *icuDataDir
;
372 icuDataDir
=u_getDataDirectory();
373 if(icuDataDir
!=NULL
&& *icuDataDir
!=0) {
374 return strcpy_returnEnd(pathBuffer
, icuDataDir
);
376 /* there is no icuDataDir either. Just return the empty pathBuffer. */
381 /* User supplied path did contain a directory portion.
382 * Copy it to the output path buffer */
383 pathLen
= (int32_t)(finalSlash
- path
+ 1);
384 uprv_memcpy(pathBuffer
, path
, pathLen
);
385 *(pathBuffer
+pathLen
) = 0;
386 return pathBuffer
+pathLen
;
390 # define DATA_TYPE "dat"
392 U_CFUNC UBool
uprv_mapFile(UDataMemory
*pData
, const char *path
) {
393 const char *inBasename
;
395 char pathBuffer
[1024];
396 const DataHeader
*pHeader
;
400 inBasename
=uprv_strrchr(path
, U_FILE_SEP_CHAR
);
401 if(inBasename
==NULL
) {
406 basename
=uprv_computeDirPath(path
, pathBuffer
);
407 if(uprv_strcmp(inBasename
, U_ICUDATA_NAME
".dat") != 0) {
408 /* must mmap file... for build */
413 UDataMemory_init(pData
); /* Clear the output struct. */
415 /* determine the length of the file */
416 if(stat(path
, &mystat
)!=0 || mystat
.st_size
<=0) {
419 length
=mystat
.st_size
;
422 fd
=open(path
, O_RDONLY
);
427 /* get a view of the mapping */
428 data
=mmap(0, length
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
429 close(fd
); /* no longer needed */
430 if(data
==MAP_FAILED
) {
433 pData
->map
= (char *)data
+ length
;
434 pData
->pHeader
=(const DataHeader
*)data
;
435 pData
->mapAddr
= data
;
440 /* ### hack: we still need to get u_getDataDirectory() fixed
441 for OS/390 (batch mode - always return "//"? )
442 and this here straightened out with LIB_PREFIX and LIB_SUFFIX (both empty?!)
443 This is probably due to the strange file system on OS/390. It's more like
444 a database with short entry names than a typical file system. */
445 /* U_ICUDATA_NAME should always have the correct name */
446 /* BUT FOR BATCH MODE IT IS AN EXCEPTION BECAUSE */
447 /* THE FIRST THREE LETTERS ARE PREASSIGNED TO THE */
449 uprv_strcpy(pathBuffer
, "IXMI" U_ICU_VERSION_SHORT
"DA");
451 /* set up the library name */
452 uprv_strcpy(basename
, LIB_PREFIX U_LIBICUDATA_NAME U_ICU_VERSION_SHORT LIB_SUFFIX
);
456 fprintf(stderr
, "dllload: %s ", pathBuffer
);
459 handle
=dllload(pathBuffer
);
462 fprintf(stderr
, " -> %08X\n", handle
);
466 /* we have a data DLL - what kind of lookup do we need here? */
467 /* try to find the Table of Contents */
468 UDataMemory_init(pData
); /* Clear the output struct. */
469 val
=dllqueryvar((dllhandle
*)handle
, U_ICUDATA_ENTRY_NAME
);
471 /* failed... so keep looking */
475 fprintf(stderr
, "dllqueryvar(%08X, %s) -> %08X\n", handle
, U_ICUDATA_ENTRY_NAME
, val
);
478 pData
->pHeader
=(const DataHeader
*)val
;
481 return FALSE
; /* no handle */
485 U_CFUNC
void uprv_unmapFile(UDataMemory
*pData
) {
486 if(pData
!=NULL
&& pData
->map
!=NULL
) {
487 uprv_free(pData
->map
);
489 pData
->mapAddr
= NULL
;
490 pData
->pHeader
= NULL
;
495 # error MAP_IMPLEMENTATION is set incorrectly