-
Notifications
You must be signed in to change notification settings - Fork 6
/
file.h
488 lines (459 loc) · 17.7 KB
/
file.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
/* *********************************************************************
* This Original Work is copyright of 51 Degrees Mobile Experts Limited.
* Copyright 2023 51 Degrees Mobile Experts Limited, Davidson House,
* Forbury Square, Reading, Berkshire, United Kingdom RG1 3EU.
*
* This Original Work is licensed under the European Union Public Licence
* (EUPL) v.1.2 and is subject to its terms as set out below.
*
* If a copy of the EUPL was not distributed with this file, You can obtain
* one at https://opensource.org/licenses/EUPL-1.2.
*
* The 'Compatible Licences' set out in the Appendix to the EUPL (as may be
* amended by the European Commission) shall be deemed incompatible for
* the purposes of the Work and the provisions of the compatibility
* clause in Article 5 of the EUPL shall not apply.
*
* If using the Work as, or as part of, a network application, by
* including the attribution notice(s) required under Article 5 of the EUPL
* in the end user terms of the application under an appropriate heading,
* such notice(s) shall fulfill the requirements of that article.
* ********************************************************************* */
/**
* @ingroup FiftyOneDegreesCommon
* @defgroup FiftyOneDegreesFile File
*
* File handle pool and simple file operations e.g. copy and delete.
*
* ## Introduction
*
* Implements a pool of file handles for use within multi-threaded environments
* where the overhead of opening and closing a file handle for each thread
* would be too great. Primarily used to load collection items from file with
* file based collections or where a cache is used.
*
* ## Creation
*
* The #fiftyoneDegreesFilePoolInit method is used to initialise a pointer to
* a #fiftyoneDegreesFilePool. A concurrency value is provided to indicate the
* maximum number of threads that will be in operation. If this value is lower
* than the actual number of threads the stack can be exhausted and a null
* pointer is returned instead of a valid file handle. The concurrency value
* must always be the same or greater than the number of threads. When compiled
* in single threaded operation a pool is not strictly required and the
* implementation maintains a simple stack for consistency of interface and to
* minimise divergent code.
*
* ## Get & Release
*
* Handles are retrieved from the pool via the #fiftyoneDegreesFileHandleGet
* method. The handle **MUST** be returned with the
* #fiftyoneDegreesFileHandleRelease method when it is finished with. The
* handle will always be open and ready for read only operation. The position
* of the handle within the source file cannot be assumed. If too many threads
* are accessing the pool simultaneously, meaning a handle cannot be secured,
* then a NULL pointer is returned.
*
* ## Free
*
* The handles are closed when the reader is released via the
* #fiftyoneDegreesFilePoolRelease method. Any memory allocated by the
* implementation for the stack is freed.
*
* ## File Operations
*
* Common file operations can also be carried out using the methods defined
* here. The supported operations are:
*
* **copy** : #fiftyoneDegreesFileCopy
*
* **create directory** : #fiftyoneDegreesFileCreateDirectory
*
* **create temp file** : #fiftyoneDegreesFileCreateTempFile
*
* **delete** : #fiftyoneDegreesFileDelete
*
* **get existing temp file** : #fiftyoneDegreesFileGetExistingTempFile
*
* **get file name** : #fiftyoneDegreesFileGetFileName
*
* **get path** : #fiftyoneDegreesFileGetPath
*
* **get size** : #fiftyoneDegreesFileGetSize
*
* **open** : #fiftyoneDegreesFileOpen
*
* **read to byte array** : #fiftyoneDegreesFileReadToByteArray
*
* **write** : #fiftyoneDegreesFileWrite
*
* ## Usage Example
*
* ```
* FIFTYONE_DEGREES_EXCEPTION_CREATE;
* fiftyoneDegreesFilePool pool;
* const char *fileName;
*
* // Initialise a file pool
* fiftyoneDegreesStatus status = fiftyoneDegreesFilePoolInit(
* &filePool,
* fileName,
* 1,
* exception);
*
* // Check that the pool was initialised successfully
* if (status == FIFTYONE_DEGREES_STATUS_SUCCESS) {
*
* // Get a file handle from the pool
* fiftyoneDegreesFileHandle *handle = fiftyoneDegreesFileHandleGet(
* &pool,
* exception);
*
* // Check that the handle was returned successfully
* if (FIFTYONE_DEGREES_EXCEPTION_OKAY) {
*
* // Do something with the file pointer in handle->file
* // ...
*
* // Release the file handle back to the pool
* fiftyoneDegreesFileHandleRelease(handle);
* }
*
* // Release the file pool
* fiftyoneDegreesFilePoolRelease(&pool);
* }
* ```
*
* ## Design
*
* To improve performance in multi-threaded operation a non locking stack is
* used where a Compare and Swap (CAS) atomic operation is used to pop and push
* handles on and off the stack. The design was adapted from the following
* article (http://nullprogram.com/blog/2014/09/02/) which explains some of the
* challenges involved including the ABA problem
* (https://en.wikipedia.org/wiki/ABA_problem). It is for this reason the head
* structure is implemented as a union between the values and the exchange
* integer. Pointers are not used as the address space for the stack is
* continuous and always very small compared to the total addressable memory
* space.
*
* @{
*/
#ifndef FIFTYONE_DEGREES_FILE_H_INCLUDED
#define FIFTYONE_DEGREES_FILE_H_INCLUDED
/* Define NDEBUG if needed, to ensure asserts are disabled in release builds */
#if !defined(DEBUG) && !defined(_DEBUG) && !defined(NDEBUG)
#define NDEBUG
#endif
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <stdint.h>
#include <stdbool.h>
#ifdef _MSC_VER
#pragma warning (push)
#pragma warning (disable: 5105)
#include <windows.h>
#pragma warning (default: 5105)
#pragma warning (pop)
#include <direct.h>
#include <tchar.h>
#else
#include <dirent.h>
#include <sys/stat.h>
#endif
#include <time.h>
#include <assert.h>
#include <limits.h>
#include <time.h>
#include "data.h"
#include "exceptions.h"
#include "status.h"
#include "memory.h"
#include "pool.h"
#include "threading.h"
#include "common.h"
/**
* Platform specific method pointer to return the current working directory.
*/
#ifdef _MSC_FULL_VER
#define GET_CURRENT_DIR _getcwd
#pragma warning (push)
#pragma warning (disable: 5105)
#include <windows.h>
#pragma warning (default: 5105)
#pragma warning (pop)
#include <direct.h>
#include <tchar.h>
#else
#define GET_CURRENT_DIR getcwd
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#endif
#define TEMP_UNIQUE_STRING_LENGTH 20
/**
* Define the max path length on the target system. For Windows this is
* `260` characters, and `4096` on most UNIX systems. The compiler defined
* values are not used here as although Windows MSVC always defined
* `_MAX_PATH`, UNIX systems do not consistently define the `PATH_MAX` macro
* resulting in undefined behavior. For this reason the length of `4096` is
* used as this is almost always the case.
*/
#ifdef _MSC_FULL_VER
#define FIFTYONE_DEGREES_FILE_MAX_PATH 260
#else
#define FIFTYONE_DEGREES_FILE_MAX_PATH 4096
#endif
/**
* File handle node in the stack of handles.
*/
typedef union fiftyone_degrees_file_handle_t {
FILE *file; /**< Open read handle to the source data file. */
fiftyoneDegreesPoolItem item; /**< The pool item with the resource. */
} fiftyoneDegreesFileHandle;
/**
* Stack of handles used to read data from a single source file.
*/
typedef struct fiftyone_degrees_file_pool_t {
fiftyoneDegreesPool pool; /**< The pool of file handles */
long length; /**< Length of the file in bytes */
} fiftyoneDegreesFilePool;
/**
* Releases the file handles contained in the pool and frees any internal
* memory used by the pool. Does not free the memory pointed to by pool.
* @param pool pointer to the stack of file handles to be release
*/
EXTERNAL void fiftyoneDegreesFilePoolRelease(fiftyoneDegreesFilePool* pool);
/**
* Opens the file path provided placing the file handle in the handle
* parameter.
* @param fileName full path to the file to open
* @param handle to be associated with the open file
* @return the result of the open operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileOpen(
const char* fileName,
FILE** handle);
/**
* Writes binary data to the file path provided, closing the file once finished.
* @param fileName full path to the file to write to
* @param data binary data to write
* @param length the number of bytes in the data to be written
* @return the result of the write operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileWrite(
const char* fileName,
const void *data,
const size_t length);
/**
* Copy a file from one location to another. If there was an error while
* creating or copying the file, then the appropriate status code will be
* returned. If the status code is anything other than
* #FIFTYONE_DEGREES_STATUS_SUCCESS then the new file will not exist.
* @param source path to the file to copy
* @param destination path to the file to create
* @return the result of the copy operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileCopy(
const char *source,
const char *destination);
/**
* Delete a file from the file system. If there was an error while removing the
* file, then the appropriate status code will be returned.
* @param fileName path to the file to be deleted
* @return the result of the delete operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileDelete(
const char *fileName);
/**
* Creates a directory with the specified path, and returns the result of the
* operation. intermediate directories will not be created.
* @param pathName path to create
* @return the result of the operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileCreateDirectory(
const char *pathName);
/**
* Iterates up the folders from the current working directory until a file
* in the sub folder dataFolderName with the name fileName is found which
* can be opened. This is assumed to be the data file required by the caller.
* Enough memory must be allocated to the destination parameter for the full
* path to be written. The maximum path length is defined by the macro
* `FIFTYONEDEGREES_FILE_MAX_PATH`.
* @param dataFolderName the name of the sub folder which is expected to
* contain the data file
* @param fileName the name of the data file
* @param destination memory to write the absolute path to if the file was
* found
* @param size the number of bytes assigned to the destination pointer
* @return the result of the operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileGetPath(
const char *dataFolderName,
const char *fileName,
char *destination,
size_t size);
/**
* Gets the path to a temporary file which is an exact copy of the master file
* if one exists. If one is found, true is returned and the path written to the
* memory pointed to by the destination parameter. If the file found has the
* same name as the master file, then false is returned to avoid the same file
* being treated as if it were a copy by external code.
* If no paths are provided, then the working directory is searched.
* @param masterFile path to the master file to find a temp version of
* @param paths list of paths to search in order of preference
* @param count number of paths in the array
* @param bytesToCompare number of from the start of the file to compare for
* equality with the master file, or -1 to compare the whole file
* @param destination memory to write the found file path to
* @return true if a copy of the master file was found, and its path written to
* destination
*/
EXTERNAL bool fiftyoneDegreesFileGetExistingTempFile(
const char *masterFile,
const char **paths,
int count,
long bytesToCompare,
const char *destination);
/**
* Finds all the temporary files which is an exact copy of the master file
* if any exist. If any are found, the method attempts to delete them. The
* number of successfully deleted temp files is returned. If any files found
* have the same name as the master file, then false is returned to avoid the
* same file being treated as if it were a copy by external code.
* If no paths are provided, then the working directory is searched. Note that
* this should not be used on Apple systems, as the checks for whether or not
* a file is in use are not implemented (all files will be deleted regardless
* of whether they are being used).
* @param masterFileName path to the master file to find a temp version of
* @param paths list of paths to search in order of preference
* @param count number of paths in the array
* @param bytesToCompare number of from the start of the file to compare for
* equality with the master file, or -1 to compare the whole file
* @return the number of matching files which have been successfully deleted
*/
EXTERNAL int fiftyoneDegreesFileDeleteUnusedTempFiles(
const char *masterFileName,
const char **paths,
int count,
long bytesToCompare);
/**
* Create a temporary file name and add it to the destination.
* @param masterFileName basename of the master file
* @param destination buffer to write the temporary file name to
* @param nameStart position of where the name should be added to the destination
* @param length length available in the buffer
* @return the result of the file name creation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileAddTempFileName(
const char* masterFileName,
char* destination,
size_t nameStart,
size_t length);
/**
* Create a temporary file containing a copy of the master file using the first
* writable path in the list of paths provided. The path which is written to
* (including file name) is written to the destination parameter which must
* contain enough memory.
* If no paths are provided, then the working directory used as the destination.
* @param masterFile full path to the file containing the master data to copy
* @param paths list of paths to use in order of preference
* @param count number of paths in the array
* @param destination memory to write the new file path to
* @param length size of the memory to be written to
* @return the result of the create operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileNewTempFile(
const char* masterFile,
const char** paths,
int count,
char* destination,
size_t length);
/**
* [DEPRECATED - Use #fiftyoneDegreesFileNewTempFile instead]
* Create a temporary file containing a copy of the master file using the first
* writable path in the list of paths provided. The path which is written to
* (including file name) is written to the destination parameter which must
* contain enough memory.
* If no paths are provided, then the working directory used as the destination.
* @param masterFile full path to the file containing the master data to copy
* @param paths list of paths to use in order of preference
* @param count number of paths in the array
* @param destination memory to write the new file path to
* @return the result of the create operation
*/
EXTERNAL DEPRECATED fiftyoneDegreesStatusCode fiftyoneDegreesFileCreateTempFile(
const char *masterFile,
const char **paths,
int count,
const char *destination);
/**
* Initialises the pool with a stack of open read only file handles all
* associated with the file name. The concurrency parameter determines the
* number of items in the stack.
* @param filePool to be initialised
* @param fileName full path to the file to open
* @param concurrency number of items in the stack
* @param exception pointer to an exception data structure to be used if an
* exception occurs. See exceptions.h.
* @return the result of the open operation
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFilePoolInit(
fiftyoneDegreesFilePool *filePool,
const char *fileName,
uint16_t concurrency,
fiftyoneDegreesException *exception);
/**
* Retrieves a read only open file handle from the pool. The handle retrieve
* must be returned to the pool using #fiftyoneDegreesFileHandleGet and must
* not be freed or closed directly.
* @param filePool to retrieve the file handle from
* @param exception pointer to an exception data structure to be used if an
* exception occurs. See exceptions.h.
* @return a read only open file handle or NULL if no available handles remain
* in the pool
*/
EXTERNAL fiftyoneDegreesFileHandle* fiftyoneDegreesFileHandleGet(
fiftyoneDegreesFilePool *filePool,
fiftyoneDegreesException *exception);
/**
* Returns a handle previously retrieved via #fiftyoneDegreesFileHandleGet back
* to the pool.
* @param handle to be returned to the pool
*/
EXTERNAL void fiftyoneDegreesFileHandleRelease(
fiftyoneDegreesFileHandle* handle);
/**
* Returns the size of a file in bytes, or -1 if the file does not exist or
* cannot be accessed.
* @param fileName path to the file
* @return size of file in bytes or -1
*/
EXTERNAL long fiftyoneDegreesFileGetSize(const char *fileName);
/**
* Reads the contents of a file into memory. The correct amount of memory will
* be allocated by the method. This memory needs to be freed by the caller
* after the data has been finished with.
* @param fileName path to the source file
* @param reader to contain the pointer to the memory and the size
* @return status code indicating whether the read was successful
*/
EXTERNAL fiftyoneDegreesStatusCode fiftyoneDegreesFileReadToByteArray(
const char *fileName,
fiftyoneDegreesMemoryReader *reader);
/**
* Resets the pool without releasing any resources.
* @param filePool to be reset.
*/
EXTERNAL void fiftyoneDegreesFilePoolReset(fiftyoneDegreesFilePool *filePool);
/**
* Gets the last, file name, segment of the full file path.
* @param filePath full path to the file.
* @return the file name from the file path.
*/
EXTERNAL const char* fiftyoneDegreesFileGetFileName(const char *filePath);
/**
* @}
*/
#endif