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