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 */
106 UDataMemory_init(pData
); /* Clear the output struct. */
108 /* open the input file */
109 #if U_PLATFORM_HAS_WINUWP_API == 0
110 file
=CreateFileA(path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
,
112 FILE_ATTRIBUTE_NORMAL
|FILE_FLAG_RANDOM_ACCESS
, NULL
);
114 // First we need to go from char to UTF-16
115 // u_UCharsToChars could work but it requires length.
116 WCHAR utf16Path
[MAX_PATH
];
118 for (i
= 0; i
< UPRV_LENGTHOF(utf16Path
); i
++)
120 utf16Path
[i
] = path
[i
];
126 if (i
>= UPRV_LENGTHOF(utf16Path
))
128 // Ran out of room, unlikely but be safe
129 utf16Path
[UPRV_LENGTHOF(utf16Path
) - 1] = '\0';
132 // TODO: Is it worth setting extended parameters to specify random access?
133 file
= CreateFile2(utf16Path
, GENERIC_READ
, FILE_SHARE_READ
, OPEN_EXISTING
, NULL
);
135 if(file
==INVALID_HANDLE_VALUE
) {
139 /* Declare and initialize a security descriptor.
140 This is required for multiuser systems on Windows 2000 SP4 and beyond */
141 // TODO: UWP does not have this function and I do not think it is required?
142 #if U_PLATFORM_HAS_WINUWP_API == 0
144 SECURITY_ATTRIBUTES mappingAttributes
;
145 SECURITY_ATTRIBUTES
*mappingAttributesPtr
= NULL
;
146 SECURITY_DESCRIPTOR securityDesc
;
148 if (InitializeSecurityDescriptor(&securityDesc
, SECURITY_DESCRIPTOR_REVISION
)) {
149 /* give the security descriptor a Null Dacl done using the "TRUE, (PACL)NULL" here */
150 if (SetSecurityDescriptorDacl(&securityDesc
, TRUE
, (PACL
)NULL
, FALSE
)) {
151 /* Make the security attributes point to the security descriptor */
152 uprv_memset(&mappingAttributes
, 0, sizeof(mappingAttributes
));
153 mappingAttributes
.nLength
= sizeof(mappingAttributes
);
154 mappingAttributes
.lpSecurityDescriptor
= &securityDesc
;
155 mappingAttributes
.bInheritHandle
= FALSE
; /* object uninheritable */
156 mappingAttributesPtr
= &mappingAttributes
;
159 /* else creating security descriptors can fail when we are on Windows 98,
160 and mappingAttributesPtr == NULL for that case. */
162 /* create an unnamed Windows file-mapping object for the specified file */
163 map
=CreateFileMapping(file
, mappingAttributesPtr
, PAGE_READONLY
, 0, 0, NULL
);
165 map
= CreateFileMappingFromApp(file
, NULL
, PAGE_READONLY
, 0, NULL
);
172 /* map a view of the file into our address space */
173 pData
->pHeader
=(const DataHeader
*)MapViewOfFile(map
, FILE_MAP_READ
, 0, 0, 0);
174 if(pData
->pHeader
==NULL
) {
183 uprv_unmapFile(UDataMemory
*pData
) {
184 if(pData
!=NULL
&& pData
->map
!=NULL
) {
185 UnmapViewOfFile(pData
->pHeader
);
186 CloseHandle(pData
->map
);
194 #elif MAP_IMPLEMENTATION==MAP_POSIX
196 uprv_mapFile(UDataMemory
*pData
, const char *path
) {
202 UDataMemory_init(pData
); /* Clear the output struct. */
204 /* determine the length of the file */
205 if(stat(path
, &mystat
)!=0 || mystat
.st_size
<=0) {
208 length
=mystat
.st_size
;
211 fd
=open(path
, O_RDONLY
);
216 /* get a view of the mapping */
217 #if U_PLATFORM != U_PF_HPUX
218 data
=mmap(0, length
, PROT_READ
, MAP_SHARED
, fd
, 0);
220 data
=mmap(0, length
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
222 close(fd
); /* no longer needed */
223 if(data
==MAP_FAILED
) {
227 pData
->map
= (char *)data
+ length
;
228 pData
->pHeader
=(const DataHeader
*)data
;
229 pData
->mapAddr
= data
;
230 #if U_PLATFORM == U_PF_IPHONE
231 posix_madvise(data
, length
, POSIX_MADV_RANDOM
);
237 uprv_unmapFile(UDataMemory
*pData
) {
238 if(pData
!=NULL
&& pData
->map
!=NULL
) {
239 size_t dataLen
= (char *)pData
->map
- (char *)pData
->mapAddr
;
240 if(munmap(pData
->mapAddr
, dataLen
)==-1) {
250 #elif MAP_IMPLEMENTATION==MAP_STDIO
251 /* copy of the filestrm.c/T_FileStream_size() implementation */
253 umap_fsize(FILE *f
) {
254 int32_t savedPos
= ftell(f
);
257 /*Changes by Bertrand A. D. doesn't affect the current position
258 goes to the end of the file before ftell*/
259 fseek(f
, 0, SEEK_END
);
260 size
= (int32_t)ftell(f
);
261 fseek(f
, savedPos
, SEEK_SET
);
266 uprv_mapFile(UDataMemory
*pData
, const char *path
) {
271 UDataMemory_init(pData
); /* Clear the output struct. */
272 /* open the input file */
273 file
=fopen(path
, "rb");
278 /* get the file length */
279 fileLength
=umap_fsize(file
);
280 if(ferror(file
) || fileLength
<=20) {
285 /* allocate the memory to hold the file data */
286 p
=uprv_malloc(fileLength
);
293 if(fileLength
!=fread(p
, 1, fileLength
, file
)) {
301 pData
->pHeader
=(const DataHeader
*)p
;
307 uprv_unmapFile(UDataMemory
*pData
) {
308 if(pData
!=NULL
&& pData
->map
!=NULL
) {
309 uprv_free(pData
->map
);
311 pData
->mapAddr
= NULL
;
312 pData
->pHeader
= NULL
;
317 #elif MAP_IMPLEMENTATION==MAP_390DLL
318 /* 390 specific Library Loading.
319 * This is the only platform left that dynamically loads an ICU Data Library.
320 * All other platforms use .data files when dynamic loading is required, but
321 * this turn out to be awkward to support in 390 batch mode.
323 * The idea here is to hide the fact that 390 is using dll loading from the
324 * rest of ICU, and make it look like there is file loading happening.
328 static char *strcpy_returnEnd(char *dest
, const char *src
)
330 while((*dest
=*src
)!=0) {
337 /*------------------------------------------------------------------------------
339 * computeDirPath given a user-supplied path of an item to be opened,
341 * - the full directory path to be used
342 * when opening the file.
343 * - Pointer to null at end of above returned path
346 * path: input path. Buffer is not altered.
347 * pathBuffer: Output buffer. Any contents are overwritten.
350 * Pointer to null termination in returned pathBuffer.
352 * TODO: This works the way ICU historically has, but the
353 * whole data fallback search path is so complicated that
354 * proabably almost no one will ever really understand it,
355 * the potential for confusion is large. (It's not just
356 * this one function, but the whole scheme.)
358 *------------------------------------------------------------------------------*/
359 static char *uprv_computeDirPath(const char *path
, char *pathBuffer
)
361 char *finalSlash
; /* Ptr to last dir separator in input path, or null if none. */
362 int32_t pathLen
; /* Length of the returned directory path */
366 finalSlash
= uprv_strrchr(path
, U_FILE_SEP_CHAR
);
370 if (finalSlash
== 0) {
371 /* No user-supplied path.
372 * Copy the ICU_DATA path to the path buffer and return that*/
373 const char *icuDataDir
;
374 icuDataDir
=u_getDataDirectory();
375 if(icuDataDir
!=NULL
&& *icuDataDir
!=0) {
376 return strcpy_returnEnd(pathBuffer
, icuDataDir
);
378 /* there is no icuDataDir either. Just return the empty pathBuffer. */
383 /* User supplied path did contain a directory portion.
384 * Copy it to the output path buffer */
385 pathLen
= (int32_t)(finalSlash
- path
+ 1);
386 uprv_memcpy(pathBuffer
, path
, pathLen
);
387 *(pathBuffer
+pathLen
) = 0;
388 return pathBuffer
+pathLen
;
392 # define DATA_TYPE "dat"
394 U_CFUNC UBool
uprv_mapFile(UDataMemory
*pData
, const char *path
) {
395 const char *inBasename
;
397 char pathBuffer
[1024];
398 const DataHeader
*pHeader
;
402 inBasename
=uprv_strrchr(path
, U_FILE_SEP_CHAR
);
403 if(inBasename
==NULL
) {
408 basename
=uprv_computeDirPath(path
, pathBuffer
);
409 if(uprv_strcmp(inBasename
, U_ICUDATA_NAME
".dat") != 0) {
410 /* must mmap file... for build */
415 UDataMemory_init(pData
); /* Clear the output struct. */
417 /* determine the length of the file */
418 if(stat(path
, &mystat
)!=0 || mystat
.st_size
<=0) {
421 length
=mystat
.st_size
;
424 fd
=open(path
, O_RDONLY
);
429 /* get a view of the mapping */
430 data
=mmap(0, length
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
431 close(fd
); /* no longer needed */
432 if(data
==MAP_FAILED
) {
435 pData
->map
= (char *)data
+ length
;
436 pData
->pHeader
=(const DataHeader
*)data
;
437 pData
->mapAddr
= data
;
442 /* ### hack: we still need to get u_getDataDirectory() fixed
443 for OS/390 (batch mode - always return "//"? )
444 and this here straightened out with LIB_PREFIX and LIB_SUFFIX (both empty?!)
445 This is probably due to the strange file system on OS/390. It's more like
446 a database with short entry names than a typical file system. */
447 /* U_ICUDATA_NAME should always have the correct name */
448 /* BUT FOR BATCH MODE IT IS AN EXCEPTION BECAUSE */
449 /* THE FIRST THREE LETTERS ARE PREASSIGNED TO THE */
451 uprv_strcpy(pathBuffer
, "IXMI" U_ICU_VERSION_SHORT
"DA");
453 /* set up the library name */
454 uprv_strcpy(basename
, LIB_PREFIX U_LIBICUDATA_NAME U_ICU_VERSION_SHORT LIB_SUFFIX
);
458 fprintf(stderr
, "dllload: %s ", pathBuffer
);
461 handle
=dllload(pathBuffer
);
464 fprintf(stderr
, " -> %08X\n", handle
);
468 /* we have a data DLL - what kind of lookup do we need here? */
469 /* try to find the Table of Contents */
470 UDataMemory_init(pData
); /* Clear the output struct. */
471 val
=dllqueryvar((dllhandle
*)handle
, U_ICUDATA_ENTRY_NAME
);
473 /* failed... so keep looking */
477 fprintf(stderr
, "dllqueryvar(%08X, %s) -> %08X\n", handle
, U_ICUDATA_ENTRY_NAME
, val
);
480 pData
->pHeader
=(const DataHeader
*)val
;
483 return FALSE
; /* no handle */
487 U_CFUNC
void uprv_unmapFile(UDataMemory
*pData
) {
488 if(pData
!=NULL
&& pData
->map
!=NULL
) {
489 uprv_free(pData
->map
);
491 pData
->mapAddr
= NULL
;
492 pData
->pHeader
= NULL
;
497 # error MAP_IMPLEMENTATION is set incorrectly