xref: /freebsd/sys/contrib/zstd/programs/util.h (revision f024dd3bf0c99ff38239ef8e92c12a9fc6bce14c)
1 /*
2  * Copyright (c) 2016-present, Przemyslaw Skibinski, Yann Collet, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under both the BSD-style license (found in the
6  * LICENSE file in the root directory of this source tree) and the GPLv2 (found
7  * in the COPYING file in the root directory of this source tree).
8  * You may select, at your option, one of the above-listed licenses.
9  */
10 
11 #ifndef UTIL_H_MODULE
12 #define UTIL_H_MODULE
13 
14 #if defined (__cplusplus)
15 extern "C" {
16 #endif
17 
18 
19 
20 /*-****************************************
21 *  Dependencies
22 ******************************************/
23 #include "platform.h"     /* PLATFORM_POSIX_VERSION, ZSTD_NANOSLEEP_SUPPORT, ZSTD_SETPRIORITY_SUPPORT */
24 #include <stdlib.h>       /* malloc */
25 #include <stddef.h>       /* size_t, ptrdiff_t */
26 #include <stdio.h>        /* fprintf */
27 #include <string.h>       /* strncmp */
28 #include <sys/types.h>    /* stat, utime */
29 #include <sys/stat.h>     /* stat, chmod */
30 #if defined(_MSC_VER)
31 #  include <sys/utime.h>  /* utime */
32 #  include <io.h>         /* _chmod */
33 #else
34 #  include <unistd.h>     /* chown, stat */
35 #  include <utime.h>      /* utime */
36 #endif
37 #include <time.h>         /* clock_t, clock, CLOCKS_PER_SEC, nanosleep */
38 #include <errno.h>
39 #include "mem.h"          /* U32, U64 */
40 
41 
42 /* ************************************************************
43 * Avoid fseek()'s 2GiB barrier with MSVC, macOS, *BSD, MinGW
44 ***************************************************************/
45 #if defined(_MSC_VER) && (_MSC_VER >= 1400)
46 #   define UTIL_fseek _fseeki64
47 #elif !defined(__64BIT__) && (PLATFORM_POSIX_VERSION >= 200112L) /* No point defining Large file for 64 bit */
48 #  define UTIL_fseek fseeko
49 #elif defined(__MINGW32__) && defined(__MSVCRT__) && !defined(__STRICT_ANSI__) && !defined(__NO_MINGW_LFS)
50 #   define UTIL_fseek fseeko64
51 #else
52 #   define UTIL_fseek fseek
53 #endif
54 
55 
56 /*-*************************************************
57 *  Sleep & priority functions: Windows - Posix - others
58 ***************************************************/
59 #if defined(_WIN32)
60 #  include <windows.h>
61 #  define SET_REALTIME_PRIORITY SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS)
62 #  define UTIL_sleep(s) Sleep(1000*s)
63 #  define UTIL_sleepMilli(milli) Sleep(milli)
64 
65 #elif PLATFORM_POSIX_VERSION > 0 /* Unix-like operating system */
66 #  include <unistd.h>   /* sleep */
67 #  define UTIL_sleep(s) sleep(s)
68 #  if ZSTD_NANOSLEEP_SUPPORT   /* necessarily defined in platform.h */
69 #      define UTIL_sleepMilli(milli) { struct timespec t; t.tv_sec=0; t.tv_nsec=milli*1000000ULL; nanosleep(&t, NULL); }
70 #  else
71 #      define UTIL_sleepMilli(milli) /* disabled */
72 #  endif
73 #  if ZSTD_SETPRIORITY_SUPPORT
74 #    include <sys/resource.h> /* setpriority */
75 #    define SET_REALTIME_PRIORITY setpriority(PRIO_PROCESS, 0, -20)
76 #  else
77 #    define SET_REALTIME_PRIORITY /* disabled */
78 #  endif
79 
80 #else  /* unknown non-unix operating systen */
81 #  define UTIL_sleep(s)          /* disabled */
82 #  define UTIL_sleepMilli(milli) /* disabled */
83 #  define SET_REALTIME_PRIORITY  /* disabled */
84 #endif
85 
86 
87 /* *************************************
88 *  Constants
89 ***************************************/
90 #define LIST_SIZE_INCREASE   (8*1024)
91 
92 
93 /*-****************************************
94 *  Compiler specifics
95 ******************************************/
96 #if defined(__INTEL_COMPILER)
97 #  pragma warning(disable : 177)    /* disable: message #177: function was declared but never referenced, useful with UTIL_STATIC */
98 #endif
99 #if defined(__GNUC__)
100 #  define UTIL_STATIC static __attribute__((unused))
101 #elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
102 #  define UTIL_STATIC static inline
103 #elif defined(_MSC_VER)
104 #  define UTIL_STATIC static __inline
105 #else
106 #  define UTIL_STATIC static  /* this version may generate warnings for unused static functions; disable the relevant warning */
107 #endif
108 
109 
110 /*-****************************************
111 *  Console log
112 ******************************************/
113 static int g_utilDisplayLevel;
114 #define UTIL_DISPLAY(...)         fprintf(stderr, __VA_ARGS__)
115 #define UTIL_DISPLAYLEVEL(l, ...) { if (g_utilDisplayLevel>=l) { UTIL_DISPLAY(__VA_ARGS__); } }
116 
117 
118 /*-****************************************
119 *  Time functions
120 ******************************************/
121 #if defined(_WIN32)   /* Windows */
122     #define UTIL_TIME_INITIALIZER { { 0, 0 } }
123     typedef LARGE_INTEGER UTIL_time_t;
124 
125     UTIL_STATIC UTIL_time_t UTIL_getTime(void) { UTIL_time_t x; QueryPerformanceCounter(&x); return x; }
126     UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd)
127     {
128         static LARGE_INTEGER ticksPerSecond;
129         static int init = 0;
130         if (!init) {
131             if (!QueryPerformanceFrequency(&ticksPerSecond))
132                 UTIL_DISPLAYLEVEL(1, "ERROR: QueryPerformanceFrequency() failure\n");
133             init = 1;
134         }
135         return 1000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart;
136     }
137     UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd)
138     {
139         static LARGE_INTEGER ticksPerSecond;
140         static int init = 0;
141         if (!init) {
142             if (!QueryPerformanceFrequency(&ticksPerSecond))
143                 UTIL_DISPLAYLEVEL(1, "ERROR: QueryPerformanceFrequency() failure\n");
144             init = 1;
145         }
146         return 1000000000ULL*(clockEnd.QuadPart - clockStart.QuadPart)/ticksPerSecond.QuadPart;
147     }
148 
149 #elif defined(__APPLE__) && defined(__MACH__)
150 
151     #include <mach/mach_time.h>
152     #define UTIL_TIME_INITIALIZER 0
153     typedef U64 UTIL_time_t;
154 
155     UTIL_STATIC UTIL_time_t UTIL_getTime(void) { return mach_absolute_time(); }
156     UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd)
157     {
158         static mach_timebase_info_data_t rate;
159         static int init = 0;
160         if (!init) {
161             mach_timebase_info(&rate);
162             init = 1;
163         }
164         return (((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom))/1000ULL;
165     }
166     UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd)
167     {
168         static mach_timebase_info_data_t rate;
169         static int init = 0;
170         if (!init) {
171             mach_timebase_info(&rate);
172             init = 1;
173         }
174         return ((clockEnd - clockStart) * (U64)rate.numer) / ((U64)rate.denom);
175     }
176 
177 #elif (PLATFORM_POSIX_VERSION >= 200112L) \
178    && (defined(__UCLIBC__)                \
179       || (defined(__GLIBC__)              \
180           && ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) \
181              || (__GLIBC__ > 2))))
182 
183     #define UTIL_TIME_INITIALIZER { 0, 0 }
184     typedef struct timespec UTIL_freq_t;
185     typedef struct timespec UTIL_time_t;
186 
187     UTIL_STATIC UTIL_time_t UTIL_getTime(void)
188     {
189         UTIL_time_t time;
190         if (clock_gettime(CLOCK_MONOTONIC, &time))
191             UTIL_DISPLAYLEVEL(1, "ERROR: Failed to get time\n");   /* we could also exit() */
192         return time;
193     }
194 
195     UTIL_STATIC UTIL_time_t UTIL_getSpanTime(UTIL_time_t begin, UTIL_time_t end)
196     {
197         UTIL_time_t diff;
198         if (end.tv_nsec < begin.tv_nsec) {
199             diff.tv_sec = (end.tv_sec - 1) - begin.tv_sec;
200             diff.tv_nsec = (end.tv_nsec + 1000000000ULL) - begin.tv_nsec;
201         } else {
202             diff.tv_sec = end.tv_sec - begin.tv_sec;
203             diff.tv_nsec = end.tv_nsec - begin.tv_nsec;
204         }
205         return diff;
206     }
207 
208     UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t begin, UTIL_time_t end)
209     {
210         UTIL_time_t const diff = UTIL_getSpanTime(begin, end);
211         U64 micro = 0;
212         micro += 1000000ULL * diff.tv_sec;
213         micro += diff.tv_nsec / 1000ULL;
214         return micro;
215     }
216 
217     UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t begin, UTIL_time_t end)
218     {
219         UTIL_time_t const diff = UTIL_getSpanTime(begin, end);
220         U64 nano = 0;
221         nano += 1000000000ULL * diff.tv_sec;
222         nano += diff.tv_nsec;
223         return nano;
224     }
225 
226 #else   /* relies on standard C (note : clock_t measurements can be wrong when using multi-threading) */
227     typedef clock_t UTIL_time_t;
228     #define UTIL_TIME_INITIALIZER 0
229     UTIL_STATIC UTIL_time_t UTIL_getTime(void) { return clock(); }
230     UTIL_STATIC U64 UTIL_getSpanTimeMicro(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; }
231     UTIL_STATIC U64 UTIL_getSpanTimeNano(UTIL_time_t clockStart, UTIL_time_t clockEnd) { return 1000000000ULL * (clockEnd - clockStart) / CLOCKS_PER_SEC; }
232 #endif
233 
234 #define SEC_TO_MICRO 1000000
235 
236 /* returns time span in microseconds */
237 UTIL_STATIC U64 UTIL_clockSpanMicro(UTIL_time_t clockStart )
238 {
239     UTIL_time_t const clockEnd = UTIL_getTime();
240     return UTIL_getSpanTimeMicro(clockStart, clockEnd);
241 }
242 
243 /* returns time span in microseconds */
244 UTIL_STATIC U64 UTIL_clockSpanNano(UTIL_time_t clockStart )
245 {
246     UTIL_time_t const clockEnd = UTIL_getTime();
247     return UTIL_getSpanTimeNano(clockStart, clockEnd);
248 }
249 
250 UTIL_STATIC void UTIL_waitForNextTick(void)
251 {
252     UTIL_time_t const clockStart = UTIL_getTime();
253     UTIL_time_t clockEnd;
254     do {
255         clockEnd = UTIL_getTime();
256     } while (UTIL_getSpanTimeNano(clockStart, clockEnd) == 0);
257 }
258 
259 
260 
261 /*-****************************************
262 *  File functions
263 ******************************************/
264 #if defined(_MSC_VER)
265     #define chmod _chmod
266     typedef struct __stat64 stat_t;
267 #else
268     typedef struct stat stat_t;
269 #endif
270 
271 
272 UTIL_STATIC int UTIL_isRegularFile(const char* infilename);
273 
274 
275 UTIL_STATIC int UTIL_setFileStat(const char *filename, stat_t *statbuf)
276 {
277     int res = 0;
278     struct utimbuf timebuf;
279 
280     if (!UTIL_isRegularFile(filename))
281         return -1;
282 
283     timebuf.actime = time(NULL);
284     timebuf.modtime = statbuf->st_mtime;
285     res += utime(filename, &timebuf);  /* set access and modification times */
286 
287 #if !defined(_WIN32)
288     res += chown(filename, statbuf->st_uid, statbuf->st_gid);  /* Copy ownership */
289 #endif
290 
291     res += chmod(filename, statbuf->st_mode & 07777);  /* Copy file permissions */
292 
293     errno = 0;
294     return -res; /* number of errors is returned */
295 }
296 
297 
298 UTIL_STATIC int UTIL_getFileStat(const char* infilename, stat_t *statbuf)
299 {
300     int r;
301 #if defined(_MSC_VER)
302     r = _stat64(infilename, statbuf);
303     if (r || !(statbuf->st_mode & S_IFREG)) return 0;   /* No good... */
304 #else
305     r = stat(infilename, statbuf);
306     if (r || !S_ISREG(statbuf->st_mode)) return 0;   /* No good... */
307 #endif
308     return 1;
309 }
310 
311 
312 UTIL_STATIC int UTIL_isRegularFile(const char* infilename)
313 {
314     stat_t statbuf;
315     return UTIL_getFileStat(infilename, &statbuf); /* Only need to know whether it is a regular file */
316 }
317 
318 
319 UTIL_STATIC U32 UTIL_isDirectory(const char* infilename)
320 {
321     int r;
322     stat_t statbuf;
323 #if defined(_MSC_VER)
324     r = _stat64(infilename, &statbuf);
325     if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
326 #else
327     r = stat(infilename, &statbuf);
328     if (!r && S_ISDIR(statbuf.st_mode)) return 1;
329 #endif
330     return 0;
331 }
332 
333 UTIL_STATIC U32 UTIL_isLink(const char* infilename)
334 {
335 /* macro guards, as defined in : https://linux.die.net/man/2/lstat */
336 #ifndef __STRICT_ANSI__
337 #if defined(_BSD_SOURCE) \
338     || (defined(_XOPEN_SOURCE) && (_XOPEN_SOURCE >= 500)) \
339     || (defined(_XOPEN_SOURCE) && defined(_XOPEN_SOURCE_EXTENDED)) \
340     || (defined(_POSIX_C_SOURCE) && (_POSIX_C_SOURCE >= 200112L)) \
341     || (defined(__APPLE__) && defined(__MACH__))
342     int r;
343     stat_t statbuf;
344     r = lstat(infilename, &statbuf);
345     if (!r && S_ISLNK(statbuf.st_mode)) return 1;
346 #endif
347 #endif
348     (void)infilename;
349     return 0;
350 }
351 
352 
353 #define UTIL_FILESIZE_UNKNOWN  ((U64)(-1))
354 UTIL_STATIC U64 UTIL_getFileSize(const char* infilename)
355 {
356     if (!UTIL_isRegularFile(infilename)) return UTIL_FILESIZE_UNKNOWN;
357     {   int r;
358 #if defined(_MSC_VER)
359         struct __stat64 statbuf;
360         r = _stat64(infilename, &statbuf);
361         if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
362 #elif defined(__MINGW32__) && defined (__MSVCRT__)
363         struct _stati64 statbuf;
364         r = _stati64(infilename, &statbuf);
365         if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
366 #else
367         struct stat statbuf;
368         r = stat(infilename, &statbuf);
369         if (r || !S_ISREG(statbuf.st_mode)) return UTIL_FILESIZE_UNKNOWN;
370 #endif
371         return (U64)statbuf.st_size;
372     }
373 }
374 
375 
376 UTIL_STATIC U64 UTIL_getTotalFileSize(const char* const * const fileNamesTable, unsigned nbFiles)
377 {
378     U64 total = 0;
379     int error = 0;
380     unsigned n;
381     for (n=0; n<nbFiles; n++) {
382         U64 const size = UTIL_getFileSize(fileNamesTable[n]);
383         error |= (size == UTIL_FILESIZE_UNKNOWN);
384         total += size;
385     }
386     return error ? UTIL_FILESIZE_UNKNOWN : total;
387 }
388 
389 
390 /*
391  * A modified version of realloc().
392  * If UTIL_realloc() fails the original block is freed.
393 */
394 UTIL_STATIC void *UTIL_realloc(void *ptr, size_t size)
395 {
396     void *newptr = realloc(ptr, size);
397     if (newptr) return newptr;
398     free(ptr);
399     return NULL;
400 }
401 
402 #ifdef _WIN32
403 #  define UTIL_HAS_CREATEFILELIST
404 
405 UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks)
406 {
407     char* path;
408     int dirLength, fnameLength, pathLength, nbFiles = 0;
409     WIN32_FIND_DATAA cFile;
410     HANDLE hFile;
411 
412     dirLength = (int)strlen(dirName);
413     path = (char*) malloc(dirLength + 3);
414     if (!path) return 0;
415 
416     memcpy(path, dirName, dirLength);
417     path[dirLength] = '\\';
418     path[dirLength+1] = '*';
419     path[dirLength+2] = 0;
420 
421     hFile=FindFirstFileA(path, &cFile);
422     if (hFile == INVALID_HANDLE_VALUE) {
423         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName);
424         return 0;
425     }
426     free(path);
427 
428     do {
429         fnameLength = (int)strlen(cFile.cFileName);
430         path = (char*) malloc(dirLength + fnameLength + 2);
431         if (!path) { FindClose(hFile); return 0; }
432         memcpy(path, dirName, dirLength);
433         path[dirLength] = '\\';
434         memcpy(path+dirLength+1, cFile.cFileName, fnameLength);
435         pathLength = dirLength+1+fnameLength;
436         path[pathLength] = 0;
437         if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
438             if (strcmp (cFile.cFileName, "..") == 0 ||
439                 strcmp (cFile.cFileName, ".") == 0) continue;
440 
441             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);  /* Recursively call "UTIL_prepareFileList" with the new path. */
442             if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
443         }
444         else if ((cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)) {
445             if (*bufStart + *pos + pathLength >= *bufEnd) {
446                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
447                 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
448                 *bufEnd = *bufStart + newListSize;
449                 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
450             }
451             if (*bufStart + *pos + pathLength < *bufEnd) {
452                 strncpy(*bufStart + *pos, path, *bufEnd - (*bufStart + *pos));
453                 *pos += pathLength + 1;
454                 nbFiles++;
455             }
456         }
457         free(path);
458     } while (FindNextFileA(hFile, &cFile));
459 
460     FindClose(hFile);
461     return nbFiles;
462 }
463 
464 #elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L)  /* opendir, readdir require POSIX.1-2001 */
465 #  define UTIL_HAS_CREATEFILELIST
466 #  include <dirent.h>       /* opendir, readdir */
467 #  include <string.h>       /* strerror, memcpy */
468 
469 UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks)
470 {
471     DIR *dir;
472     struct dirent *entry;
473     char* path;
474     int dirLength, fnameLength, pathLength, nbFiles = 0;
475 
476     if (!(dir = opendir(dirName))) {
477         UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
478         return 0;
479     }
480 
481     dirLength = (int)strlen(dirName);
482     errno = 0;
483     while ((entry = readdir(dir)) != NULL) {
484         if (strcmp (entry->d_name, "..") == 0 ||
485             strcmp (entry->d_name, ".") == 0) continue;
486         fnameLength = (int)strlen(entry->d_name);
487         path = (char*) malloc(dirLength + fnameLength + 2);
488         if (!path) { closedir(dir); return 0; }
489         memcpy(path, dirName, dirLength);
490 
491         path[dirLength] = '/';
492         memcpy(path+dirLength+1, entry->d_name, fnameLength);
493         pathLength = dirLength+1+fnameLength;
494         path[pathLength] = 0;
495 
496         if (!followLinks && UTIL_isLink(path)) {
497             UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path);
498             continue;
499         }
500 
501         if (UTIL_isDirectory(path)) {
502             nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);  /* Recursively call "UTIL_prepareFileList" with the new path. */
503             if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
504         } else {
505             if (*bufStart + *pos + pathLength >= *bufEnd) {
506                 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
507                 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
508                 *bufEnd = *bufStart + newListSize;
509                 if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
510             }
511             if (*bufStart + *pos + pathLength < *bufEnd) {
512                 strncpy(*bufStart + *pos, path, *bufEnd - (*bufStart + *pos));
513                 *pos += pathLength + 1;
514                 nbFiles++;
515             }
516         }
517         free(path);
518         errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */
519     }
520 
521     if (errno != 0) {
522         UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s\n", dirName, strerror(errno));
523         free(*bufStart);
524         *bufStart = NULL;
525     }
526     closedir(dir);
527     return nbFiles;
528 }
529 
530 #else
531 
532 UTIL_STATIC int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks)
533 {
534     (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks;
535     UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE)\n", dirName);
536     return 0;
537 }
538 
539 #endif /* #ifdef _WIN32 */
540 
541 /*
542  * UTIL_createFileList - takes a list of files and directories (params: inputNames, inputNamesNb), scans directories,
543  *                       and returns a new list of files (params: return value, allocatedBuffer, allocatedNamesNb).
544  * After finishing usage of the list the structures should be freed with UTIL_freeFileList(params: return value, allocatedBuffer)
545  * In case of error UTIL_createFileList returns NULL and UTIL_freeFileList should not be called.
546  */
547 UTIL_STATIC const char**
548 UTIL_createFileList(const char **inputNames, unsigned inputNamesNb,
549                     char** allocatedBuffer, unsigned* allocatedNamesNb,
550                     int followLinks)
551 {
552     size_t pos;
553     unsigned i, nbFiles;
554     char* buf = (char*)malloc(LIST_SIZE_INCREASE);
555     char* bufend = buf + LIST_SIZE_INCREASE;
556     const char** fileTable;
557 
558     if (!buf) return NULL;
559 
560     for (i=0, pos=0, nbFiles=0; i<inputNamesNb; i++) {
561         if (!UTIL_isDirectory(inputNames[i])) {
562             size_t const len = strlen(inputNames[i]);
563             if (buf + pos + len >= bufend) {
564                 ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE;
565                 buf = (char*)UTIL_realloc(buf, newListSize);
566                 bufend = buf + newListSize;
567                 if (!buf) return NULL;
568             }
569             if (buf + pos + len < bufend) {
570                 strncpy(buf + pos, inputNames[i], bufend - (buf + pos));
571                 pos += len + 1;
572                 nbFiles++;
573             }
574         } else {
575             nbFiles += UTIL_prepareFileList(inputNames[i], &buf, &pos, &bufend, followLinks);
576             if (buf == NULL) return NULL;
577     }   }
578 
579     if (nbFiles == 0) { free(buf); return NULL; }
580 
581     fileTable = (const char**)malloc((nbFiles+1) * sizeof(const char*));
582     if (!fileTable) { free(buf); return NULL; }
583 
584     for (i=0, pos=0; i<nbFiles; i++) {
585         fileTable[i] = buf + pos;
586         pos += strlen(fileTable[i]) + 1;
587     }
588 
589     if (buf + pos > bufend) { free(buf); free((void*)fileTable); return NULL; }
590 
591     *allocatedBuffer = buf;
592     *allocatedNamesNb = nbFiles;
593 
594     return fileTable;
595 }
596 
597 
598 UTIL_STATIC void UTIL_freeFileList(const char** filenameTable, char* allocatedBuffer)
599 {
600     if (allocatedBuffer) free(allocatedBuffer);
601     if (filenameTable) free((void*)filenameTable);
602 }
603 
604 /* count the number of physical cores */
605 #if defined(_WIN32) || defined(WIN32)
606 
607 #include <windows.h>
608 
609 typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
610 
611 UTIL_STATIC int UTIL_countPhysicalCores(void)
612 {
613     static int numPhysicalCores = 0;
614     if (numPhysicalCores != 0) return numPhysicalCores;
615 
616     {   LPFN_GLPI glpi;
617         BOOL done = FALSE;
618         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
619         PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
620         DWORD returnLength = 0;
621         size_t byteOffset = 0;
622 
623         glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
624                                          "GetLogicalProcessorInformation");
625 
626         if (glpi == NULL) {
627             goto failed;
628         }
629 
630         while(!done) {
631             DWORD rc = glpi(buffer, &returnLength);
632             if (FALSE == rc) {
633                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
634                     if (buffer)
635                         free(buffer);
636                     buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
637 
638                     if (buffer == NULL) {
639                         perror("zstd");
640                         exit(1);
641                     }
642                 } else {
643                     /* some other error */
644                     goto failed;
645                 }
646             } else {
647                 done = TRUE;
648             }
649         }
650 
651         ptr = buffer;
652 
653         while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
654 
655             if (ptr->Relationship == RelationProcessorCore) {
656                 numPhysicalCores++;
657             }
658 
659             ptr++;
660             byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
661         }
662 
663         free(buffer);
664 
665         return numPhysicalCores;
666     }
667 
668 failed:
669     /* try to fall back on GetSystemInfo */
670     {   SYSTEM_INFO sysinfo;
671         GetSystemInfo(&sysinfo);
672         numPhysicalCores = sysinfo.dwNumberOfProcessors;
673         if (numPhysicalCores == 0) numPhysicalCores = 1; /* just in case */
674     }
675     return numPhysicalCores;
676 }
677 
678 #elif defined(__APPLE__)
679 
680 #include <sys/sysctl.h>
681 
682 /* Use apple-provided syscall
683  * see: man 3 sysctl */
684 UTIL_STATIC int UTIL_countPhysicalCores(void)
685 {
686     static S32 numPhysicalCores = 0; /* apple specifies int32_t */
687     if (numPhysicalCores != 0) return numPhysicalCores;
688 
689     {   size_t size = sizeof(S32);
690         int const ret = sysctlbyname("hw.physicalcpu", &numPhysicalCores, &size, NULL, 0);
691         if (ret != 0) {
692             if (errno == ENOENT) {
693                 /* entry not present, fall back on 1 */
694                 numPhysicalCores = 1;
695             } else {
696                 perror("zstd: can't get number of physical cpus");
697                 exit(1);
698             }
699         }
700 
701         return numPhysicalCores;
702     }
703 }
704 
705 #elif defined(__linux__)
706 
707 /* parse /proc/cpuinfo
708  * siblings / cpu cores should give hyperthreading ratio
709  * otherwise fall back on sysconf */
710 UTIL_STATIC int UTIL_countPhysicalCores(void)
711 {
712     static int numPhysicalCores = 0;
713 
714     if (numPhysicalCores != 0) return numPhysicalCores;
715 
716     numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
717     if (numPhysicalCores == -1) {
718         /* value not queryable, fall back on 1 */
719         return numPhysicalCores = 1;
720     }
721 
722     /* try to determine if there's hyperthreading */
723     {   FILE* const cpuinfo = fopen("/proc/cpuinfo", "r");
724 #define BUF_SIZE 80
725         char buff[BUF_SIZE];
726 
727         int siblings = 0;
728         int cpu_cores = 0;
729         int ratio = 1;
730 
731         if (cpuinfo == NULL) {
732             /* fall back on the sysconf value */
733             return numPhysicalCores;
734         }
735 
736         /* assume the cpu cores/siblings values will be constant across all
737          * present processors */
738         while (!feof(cpuinfo)) {
739             if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) {
740                 if (strncmp(buff, "siblings", 8) == 0) {
741                     const char* const sep = strchr(buff, ':');
742                     if (*sep == '\0') {
743                         /* formatting was broken? */
744                         goto failed;
745                     }
746 
747                     siblings = atoi(sep + 1);
748                 }
749                 if (strncmp(buff, "cpu cores", 9) == 0) {
750                     const char* const sep = strchr(buff, ':');
751                     if (*sep == '\0') {
752                         /* formatting was broken? */
753                         goto failed;
754                     }
755 
756                     cpu_cores = atoi(sep + 1);
757                 }
758             } else if (ferror(cpuinfo)) {
759                 /* fall back on the sysconf value */
760                 goto failed;
761             }
762         }
763         if (siblings && cpu_cores) {
764             ratio = siblings / cpu_cores;
765         }
766 failed:
767         fclose(cpuinfo);
768         return numPhysicalCores = numPhysicalCores / ratio;
769     }
770 }
771 
772 #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
773 
774 /* Use apple-provided syscall
775  * see: man 3 sysctl */
776 UTIL_STATIC int UTIL_countPhysicalCores(void)
777 {
778     static int numPhysicalCores = 0;
779 
780     if (numPhysicalCores != 0) return numPhysicalCores;
781 
782     numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
783     if (numPhysicalCores == -1) {
784         /* value not queryable, fall back on 1 */
785         return numPhysicalCores = 1;
786     }
787     return numPhysicalCores;
788 }
789 
790 #else
791 
792 UTIL_STATIC int UTIL_countPhysicalCores(void)
793 {
794     /* assume 1 */
795     return 1;
796 }
797 
798 #endif
799 
800 #if defined (__cplusplus)
801 }
802 #endif
803 
804 #endif /* UTIL_H_MODULE */
805