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