blob: e5c937864ee232d227437a0d8e888f75c08d38df [file] [log] [blame]
Rohit Jainf881ee82018-10-11 12:52:19 -07001/*
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#if defined (__cplusplus)
12extern "C" {
13#endif
14
15
16/*-****************************************
17* Dependencies
18******************************************/
Yann Colletffba1422018-12-20 14:30:30 -080019#include "util.h" /* note : ensure that platform.h is included first ! */
Yann Collet173ef9d2018-12-19 18:30:57 -080020#include <errno.h>
Yann Collet72dbf1b2018-12-20 12:27:12 -080021#include <assert.h>
Yann Collet173ef9d2018-12-19 18:30:57 -080022
Sen Huang62616c42019-09-06 13:20:50 -070023#if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__)
24#include <direct.h> /* needed for _mkdir in windows */
25#endif
Rohit Jainf881ee82018-10-11 12:52:19 -070026
Yann Collet173ef9d2018-12-19 18:30:57 -080027int UTIL_fileExist(const char* filename)
28{
29 stat_t statbuf;
Yann Collet105fa952018-12-20 09:16:40 -080030#if defined(_MSC_VER)
31 int const stat_error = _stat64(filename, &statbuf);
32#else
33 int const stat_error = stat(filename, &statbuf);
34#endif
35 return !stat_error;
Yann Collet173ef9d2018-12-19 18:30:57 -080036}
37
Rohit Jaind6d240f2018-10-11 15:07:12 -070038int UTIL_isRegularFile(const char* infilename)
39{
40 stat_t statbuf;
41 return UTIL_getFileStat(infilename, &statbuf); /* Only need to know whether it is a regular file */
42}
43
44int UTIL_getFileStat(const char* infilename, stat_t *statbuf)
45{
46 int r;
47#if defined(_MSC_VER)
48 r = _stat64(infilename, statbuf);
49 if (r || !(statbuf->st_mode & S_IFREG)) return 0; /* No good... */
50#else
51 r = stat(infilename, statbuf);
52 if (r || !S_ISREG(statbuf->st_mode)) return 0; /* No good... */
53#endif
54 return 1;
55}
56
57int UTIL_setFileStat(const char *filename, stat_t *statbuf)
58{
59 int res = 0;
60 struct utimbuf timebuf;
61
62 if (!UTIL_isRegularFile(filename))
63 return -1;
64
65 timebuf.actime = time(NULL);
66 timebuf.modtime = statbuf->st_mtime;
67 res += utime(filename, &timebuf); /* set access and modification times */
68
69#if !defined(_WIN32)
70 res += chown(filename, statbuf->st_uid, statbuf->st_gid); /* Copy ownership */
71#endif
72
73 res += chmod(filename, statbuf->st_mode & 07777); /* Copy file permissions */
74
75 errno = 0;
76 return -res; /* number of errors is returned */
77}
Rohit Jainf881ee82018-10-11 12:52:19 -070078
79U32 UTIL_isDirectory(const char* infilename)
80{
81 int r;
82 stat_t statbuf;
83#if defined(_MSC_VER)
84 r = _stat64(infilename, &statbuf);
85 if (!r && (statbuf.st_mode & _S_IFDIR)) return 1;
86#else
87 r = stat(infilename, &statbuf);
88 if (!r && S_ISDIR(statbuf.st_mode)) return 1;
89#endif
90 return 0;
91}
92
Sen Huangf80437c2019-10-02 11:08:20 -040093int UTIL_compareStr(const void *p1, const void *p2) {
94 return strcmp(* (char * const *) p1, * (char * const *) p2);
95}
Sen Huanga9c807a2019-09-06 10:17:04 -070096
Sen Huangf80437c2019-10-02 11:08:20 -040097int UTIL_checkFilenameCollisions(char** dstFilenameTable, unsigned nbFiles) {
98 char** dstFilenameTableSorted;
99 char* prevElem;
100 unsigned u;
101
102 dstFilenameTableSorted = (char**) malloc(sizeof(char*) * nbFiles);
103 if (!dstFilenameTableSorted) {
104 UTIL_DISPLAYLEVEL(1, "Unable to malloc new str array, not checking for name collisions\n");
105 return 1;
106 }
107
108 for (u = 0; u < nbFiles; ++u) {
109 dstFilenameTableSorted[u] = dstFilenameTable[u];
110 }
111 qsort(dstFilenameTableSorted, nbFiles, sizeof(char*), UTIL_compareStr);
112 prevElem = dstFilenameTableSorted[0];
113 for (u = 1; u < nbFiles; ++u) {
114 if (strcmp(prevElem, dstFilenameTableSorted[u]) == 0) {
115 UTIL_DISPLAYLEVEL(1, "WARNING: Two files have same filename as source : %s\n", prevElem);
116 }
117 prevElem = dstFilenameTableSorted[u];
118 }
119
120 free(dstFilenameTableSorted);
Sen Huang7f98b462019-09-05 16:03:35 -0700121 return 0;
122}
123
Sen Huangf80437c2019-10-02 11:08:20 -0400124void UTIL_createDestinationDirTable(char** dstFilenameTable, const char** filenameTable,
125 const unsigned nbFiles, const char* outDirName, const int compressing)
Sen Huang7f98b462019-09-05 16:03:35 -0700126{
127 unsigned u;
Sen Huangf80437c2019-10-02 11:08:20 -0400128 const char* c;
129 #if defined(_MSC_VER) || defined(__MINGW32__) || defined (__MSVCRT__) /* windows support */
130 c = "\\";
131 #else
132 c = "/";
133 #endif
Sen Huang7f98b462019-09-05 16:03:35 -0700134
Sen Huang7f98b462019-09-05 16:03:35 -0700135 for (u = 0; u < nbFiles; ++u) {
Sen Huangf80437c2019-10-02 11:08:20 -0400136 char* filename, *filenameBegin;
Sen Huang7f98b462019-09-05 16:03:35 -0700137 size_t finalPathLen;
138 finalPathLen = strlen(outDirName);
Sen Huangf80437c2019-10-02 11:08:20 -0400139 filenameBegin = strrchr(filenameTable[u], c[0]);
140 if (filenameBegin == NULL) {
141 filename = strdup(filenameTable[u]);
142 } else {
143 filename = strdup(filenameBegin+1);
144 }
145
Sen Huang7f98b462019-09-05 16:03:35 -0700146 finalPathLen += strlen(filename);
Sen Huangf80437c2019-10-02 11:08:20 -0400147 dstFilenameTable[u] = compressing ?
148 (char*) malloc((finalPathLen+6) * sizeof(char)) /* 4 more bytes for .zst suffix */
149 : (char*) malloc((finalPathLen+2) * sizeof(char));
150 if (!dstFilenameTable[u]) {
151 UTIL_DISPLAYLEVEL(1, "Unable to allocate space for file destination str\n");
152 continue;
153 }
154
Sen Huang7f98b462019-09-05 16:03:35 -0700155 strcpy(dstFilenameTable[u], outDirName);
Sen Huangf80437c2019-10-02 11:08:20 -0400156 if (outDirName[strlen(outDirName)-1] == c[0]) {
157 strcat(dstFilenameTable[u], filename);
158 } else {
159 strcat(dstFilenameTable[u], c);
160 strcat(dstFilenameTable[u], filename);
161 }
Sen Huang7f98b462019-09-05 16:03:35 -0700162
Sen Huangf80437c2019-10-02 11:08:20 -0400163 free(filename);
164 }
Sen Huang30bff502019-09-06 11:10:53 -0700165
Sen Huangf80437c2019-10-02 11:08:20 -0400166 if (UTIL_checkFilenameCollisions(dstFilenameTable, nbFiles)) {
167 UTIL_DISPLAYLEVEL(1, "Checking for filename collisions failed");
Sen Huang30bff502019-09-06 11:10:53 -0700168 }
169}
170
Sen Huang7f98b462019-09-05 16:03:35 -0700171void UTIL_freeDestinationFilenameTable(char** dstDirTable, unsigned nbFiles) {
172 unsigned u;
Sen Huang6beb3c02019-09-05 17:56:24 -0700173 for (u = 0; u < nbFiles; ++u) {
174 if (dstDirTable[u] != NULL)
175 free(dstDirTable[u]);
176 }
177 if (dstDirTable != NULL) free((void*)dstDirTable);
Sen Huang7f98b462019-09-05 16:03:35 -0700178}
179
shakeelraoe5811e52019-03-23 19:04:56 -0700180int UTIL_isSameFile(const char* file1, const char* file2)
181{
182#if defined(_MSC_VER)
183 /* note : Visual does not support file identification by inode.
184 * The following work-around is limited to detecting exact name repetition only,
185 * aka `filename` is considered different from `subdir/../filename` */
186 return !strcmp(file1, file2);
187#else
188 stat_t file1Stat;
189 stat_t file2Stat;
190 return UTIL_getFileStat(file1, &file1Stat)
191 && UTIL_getFileStat(file2, &file2Stat)
192 && (file1Stat.st_dev == file2Stat.st_dev)
193 && (file1Stat.st_ino == file2Stat.st_ino);
194#endif
195}
196
Rohit Jainf881ee82018-10-11 12:52:19 -0700197U32 UTIL_isLink(const char* infilename)
198{
199/* macro guards, as defined in : https://linux.die.net/man/2/lstat */
W. Felix Handted2c48042019-06-07 15:31:33 -0400200#if PLATFORM_POSIX_VERSION >= 200112L
Rohit Jainf881ee82018-10-11 12:52:19 -0700201 int r;
202 stat_t statbuf;
203 r = lstat(infilename, &statbuf);
204 if (!r && S_ISLNK(statbuf.st_mode)) return 1;
205#endif
Rohit Jainf881ee82018-10-11 12:52:19 -0700206 (void)infilename;
207 return 0;
208}
209
210U64 UTIL_getFileSize(const char* infilename)
211{
212 if (!UTIL_isRegularFile(infilename)) return UTIL_FILESIZE_UNKNOWN;
213 { int r;
214#if defined(_MSC_VER)
215 struct __stat64 statbuf;
216 r = _stat64(infilename, &statbuf);
217 if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
218#elif defined(__MINGW32__) && defined (__MSVCRT__)
219 struct _stati64 statbuf;
220 r = _stati64(infilename, &statbuf);
221 if (r || !(statbuf.st_mode & S_IFREG)) return UTIL_FILESIZE_UNKNOWN;
222#else
223 struct stat statbuf;
224 r = stat(infilename, &statbuf);
225 if (r || !S_ISREG(statbuf.st_mode)) return UTIL_FILESIZE_UNKNOWN;
226#endif
227 return (U64)statbuf.st_size;
228 }
229}
230
231
232U64 UTIL_getTotalFileSize(const char* const * const fileNamesTable, unsigned nbFiles)
233{
234 U64 total = 0;
235 int error = 0;
236 unsigned n;
237 for (n=0; n<nbFiles; n++) {
238 U64 const size = UTIL_getFileSize(fileNamesTable[n]);
239 error |= (size == UTIL_FILESIZE_UNKNOWN);
240 total += size;
241 }
242 return error ? UTIL_FILESIZE_UNKNOWN : total;
243}
244
Rohit Jainc7251e52018-10-11 18:05:15 -0700245#ifdef _WIN32
Rohit Jain705e0b12018-10-11 15:51:57 -0700246int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks)
247{
248 char* path;
249 int dirLength, fnameLength, pathLength, nbFiles = 0;
250 WIN32_FIND_DATAA cFile;
251 HANDLE hFile;
252
253 dirLength = (int)strlen(dirName);
254 path = (char*) malloc(dirLength + 3);
255 if (!path) return 0;
256
257 memcpy(path, dirName, dirLength);
258 path[dirLength] = '\\';
259 path[dirLength+1] = '*';
260 path[dirLength+2] = 0;
261
262 hFile=FindFirstFileA(path, &cFile);
263 if (hFile == INVALID_HANDLE_VALUE) {
264 UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s'\n", dirName);
265 return 0;
266 }
267 free(path);
268
269 do {
270 fnameLength = (int)strlen(cFile.cFileName);
271 path = (char*) malloc(dirLength + fnameLength + 2);
272 if (!path) { FindClose(hFile); return 0; }
273 memcpy(path, dirName, dirLength);
274 path[dirLength] = '\\';
275 memcpy(path+dirLength+1, cFile.cFileName, fnameLength);
276 pathLength = dirLength+1+fnameLength;
277 path[pathLength] = 0;
278 if (cFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
Yann Collet72dbf1b2018-12-20 12:27:12 -0800279 if ( strcmp (cFile.cFileName, "..") == 0
280 || strcmp (cFile.cFileName, ".") == 0 )
281 continue;
282 /* Recursively call "UTIL_prepareFileList" with the new path. */
283 nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks);
Rohit Jain705e0b12018-10-11 15:51:57 -0700284 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
Yann Collet72dbf1b2018-12-20 12:27:12 -0800285 } else if ( (cFile.dwFileAttributes & FILE_ATTRIBUTE_NORMAL)
286 || (cFile.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)
287 || (cFile.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) ) {
Rohit Jain705e0b12018-10-11 15:51:57 -0700288 if (*bufStart + *pos + pathLength >= *bufEnd) {
Yann Collet72dbf1b2018-12-20 12:27:12 -0800289 ptrdiff_t const newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
Rohit Jain705e0b12018-10-11 15:51:57 -0700290 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
Rohit Jain705e0b12018-10-11 15:51:57 -0700291 if (*bufStart == NULL) { free(path); FindClose(hFile); return 0; }
Yann Collet72dbf1b2018-12-20 12:27:12 -0800292 *bufEnd = *bufStart + newListSize;
Rohit Jain705e0b12018-10-11 15:51:57 -0700293 }
294 if (*bufStart + *pos + pathLength < *bufEnd) {
Yann Collet72dbf1b2018-12-20 12:27:12 -0800295 memcpy(*bufStart + *pos, path, pathLength+1 /* include final \0 */);
Rohit Jain705e0b12018-10-11 15:51:57 -0700296 *pos += pathLength + 1;
297 nbFiles++;
298 }
299 }
300 free(path);
301 } while (FindNextFileA(hFile, &cFile));
302
303 FindClose(hFile);
304 return nbFiles;
305}
306
307#elif defined(__linux__) || (PLATFORM_POSIX_VERSION >= 200112L) /* opendir, readdir require POSIX.1-2001 */
308
309int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks)
310{
311 DIR *dir;
312 struct dirent *entry;
313 char* path;
314 int dirLength, fnameLength, pathLength, nbFiles = 0;
315
316 if (!(dir = opendir(dirName))) {
317 UTIL_DISPLAYLEVEL(1, "Cannot open directory '%s': %s\n", dirName, strerror(errno));
318 return 0;
319 }
320
321 dirLength = (int)strlen(dirName);
322 errno = 0;
323 while ((entry = readdir(dir)) != NULL) {
324 if (strcmp (entry->d_name, "..") == 0 ||
325 strcmp (entry->d_name, ".") == 0) continue;
326 fnameLength = (int)strlen(entry->d_name);
327 path = (char*) malloc(dirLength + fnameLength + 2);
328 if (!path) { closedir(dir); return 0; }
329 memcpy(path, dirName, dirLength);
330
331 path[dirLength] = '/';
332 memcpy(path+dirLength+1, entry->d_name, fnameLength);
333 pathLength = dirLength+1+fnameLength;
334 path[pathLength] = 0;
335
336 if (!followLinks && UTIL_isLink(path)) {
337 UTIL_DISPLAYLEVEL(2, "Warning : %s is a symbolic link, ignoring\n", path);
LeeYoung624793b94b2019-07-25 21:07:57 +0800338 free(path);
Rohit Jain705e0b12018-10-11 15:51:57 -0700339 continue;
340 }
341
342 if (UTIL_isDirectory(path)) {
343 nbFiles += UTIL_prepareFileList(path, bufStart, pos, bufEnd, followLinks); /* Recursively call "UTIL_prepareFileList" with the new path. */
344 if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
345 } else {
346 if (*bufStart + *pos + pathLength >= *bufEnd) {
347 ptrdiff_t newListSize = (*bufEnd - *bufStart) + LIST_SIZE_INCREASE;
348 *bufStart = (char*)UTIL_realloc(*bufStart, newListSize);
349 *bufEnd = *bufStart + newListSize;
350 if (*bufStart == NULL) { free(path); closedir(dir); return 0; }
351 }
352 if (*bufStart + *pos + pathLength < *bufEnd) {
Yann Collet72dbf1b2018-12-20 12:27:12 -0800353 memcpy(*bufStart + *pos, path, pathLength + 1); /* with final \0 */
Rohit Jain705e0b12018-10-11 15:51:57 -0700354 *pos += pathLength + 1;
355 nbFiles++;
356 }
357 }
358 free(path);
359 errno = 0; /* clear errno after UTIL_isDirectory, UTIL_prepareFileList */
360 }
361
362 if (errno != 0) {
363 UTIL_DISPLAYLEVEL(1, "readdir(%s) error: %s\n", dirName, strerror(errno));
364 free(*bufStart);
365 *bufStart = NULL;
366 }
367 closedir(dir);
368 return nbFiles;
369}
370
371#else
372
373int UTIL_prepareFileList(const char *dirName, char** bufStart, size_t* pos, char** bufEnd, int followLinks)
374{
375 (void)bufStart; (void)bufEnd; (void)pos; (void)followLinks;
376 UTIL_DISPLAYLEVEL(1, "Directory %s ignored (compiled without _WIN32 or _POSIX_C_SOURCE)\n", dirName);
377 return 0;
378}
379
380#endif /* #ifdef _WIN32 */
381
382/*
383 * UTIL_createFileList - takes a list of files and directories (params: inputNames, inputNamesNb), scans directories,
384 * and returns a new list of files (params: return value, allocatedBuffer, allocatedNamesNb).
385 * After finishing usage of the list the structures should be freed with UTIL_freeFileList(params: return value, allocatedBuffer)
386 * In case of error UTIL_createFileList returns NULL and UTIL_freeFileList should not be called.
387 */
388const char**
389UTIL_createFileList(const char **inputNames, unsigned inputNamesNb,
390 char** allocatedBuffer, unsigned* allocatedNamesNb,
391 int followLinks)
392{
393 size_t pos;
394 unsigned i, nbFiles;
395 char* buf = (char*)malloc(LIST_SIZE_INCREASE);
396 char* bufend = buf + LIST_SIZE_INCREASE;
397 const char** fileTable;
398
399 if (!buf) return NULL;
400
401 for (i=0, pos=0, nbFiles=0; i<inputNamesNb; i++) {
402 if (!UTIL_isDirectory(inputNames[i])) {
403 size_t const len = strlen(inputNames[i]);
404 if (buf + pos + len >= bufend) {
405 ptrdiff_t newListSize = (bufend - buf) + LIST_SIZE_INCREASE;
406 buf = (char*)UTIL_realloc(buf, newListSize);
407 bufend = buf + newListSize;
408 if (!buf) return NULL;
409 }
410 if (buf + pos + len < bufend) {
Yann Collet72dbf1b2018-12-20 12:27:12 -0800411 memcpy(buf+pos, inputNames[i], len+1); /* with final \0 */
Rohit Jain705e0b12018-10-11 15:51:57 -0700412 pos += len + 1;
413 nbFiles++;
414 }
415 } else {
416 nbFiles += UTIL_prepareFileList(inputNames[i], &buf, &pos, &bufend, followLinks);
417 if (buf == NULL) return NULL;
418 } }
419
420 if (nbFiles == 0) { free(buf); return NULL; }
421
422 fileTable = (const char**)malloc((nbFiles+1) * sizeof(const char*));
423 if (!fileTable) { free(buf); return NULL; }
424
425 for (i=0, pos=0; i<nbFiles; i++) {
426 fileTable[i] = buf + pos;
427 pos += strlen(fileTable[i]) + 1;
428 }
429
430 if (buf + pos > bufend) { free(buf); free((void*)fileTable); return NULL; }
431
432 *allocatedBuffer = buf;
433 *allocatedNamesNb = nbFiles;
434
435 return fileTable;
436}
437
Yann Collet59a71162019-04-10 12:37:03 -0700438
Rohit Jaind6d240f2018-10-11 15:07:12 -0700439/*-****************************************
Rohit Jaina47f6e62018-10-11 16:51:29 -0700440* Console log
441******************************************/
442int g_utilDisplayLevel;
443
Yann Collet72dbf1b2018-12-20 12:27:12 -0800444
Yann Collet59a71162019-04-10 12:37:03 -0700445
Rohit Jaina47f6e62018-10-11 16:51:29 -0700446/*-****************************************
Yann Collet59a71162019-04-10 12:37:03 -0700447* count the number of physical cores
Rohit Jaind6d240f2018-10-11 15:07:12 -0700448******************************************/
Rohit Jainc7251e52018-10-11 18:05:15 -0700449
Rohit Jain91b2fed2018-10-11 17:34:47 -0700450#if defined(_WIN32) || defined(WIN32)
451
452#include <windows.h>
453
454typedef BOOL(WINAPI* LPFN_GLPI)(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION, PDWORD);
455
456int UTIL_countPhysicalCores(void)
457{
458 static int numPhysicalCores = 0;
459 if (numPhysicalCores != 0) return numPhysicalCores;
460
461 { LPFN_GLPI glpi;
462 BOOL done = FALSE;
463 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL;
464 PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = NULL;
465 DWORD returnLength = 0;
466 size_t byteOffset = 0;
467
468 glpi = (LPFN_GLPI)GetProcAddress(GetModuleHandle(TEXT("kernel32")),
469 "GetLogicalProcessorInformation");
470
471 if (glpi == NULL) {
472 goto failed;
473 }
474
475 while(!done) {
476 DWORD rc = glpi(buffer, &returnLength);
477 if (FALSE == rc) {
478 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
479 if (buffer)
480 free(buffer);
481 buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc(returnLength);
482
483 if (buffer == NULL) {
484 perror("zstd");
485 exit(1);
486 }
487 } else {
488 /* some other error */
489 goto failed;
490 }
491 } else {
492 done = TRUE;
493 }
494 }
495
496 ptr = buffer;
497
498 while (byteOffset + sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) <= returnLength) {
499
500 if (ptr->Relationship == RelationProcessorCore) {
501 numPhysicalCores++;
502 }
503
504 ptr++;
505 byteOffset += sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
506 }
507
508 free(buffer);
509
510 return numPhysicalCores;
511 }
512
513failed:
514 /* try to fall back on GetSystemInfo */
515 { SYSTEM_INFO sysinfo;
516 GetSystemInfo(&sysinfo);
517 numPhysicalCores = sysinfo.dwNumberOfProcessors;
518 if (numPhysicalCores == 0) numPhysicalCores = 1; /* just in case */
519 }
520 return numPhysicalCores;
521}
522
523#elif defined(__APPLE__)
524
525#include <sys/sysctl.h>
526
527/* Use apple-provided syscall
528 * see: man 3 sysctl */
529int UTIL_countPhysicalCores(void)
530{
531 static S32 numPhysicalCores = 0; /* apple specifies int32_t */
532 if (numPhysicalCores != 0) return numPhysicalCores;
533
534 { size_t size = sizeof(S32);
535 int const ret = sysctlbyname("hw.physicalcpu", &numPhysicalCores, &size, NULL, 0);
536 if (ret != 0) {
537 if (errno == ENOENT) {
538 /* entry not present, fall back on 1 */
539 numPhysicalCores = 1;
540 } else {
541 perror("zstd: can't get number of physical cpus");
542 exit(1);
543 }
544 }
545
546 return numPhysicalCores;
547 }
548}
549
550#elif defined(__linux__)
551
552/* parse /proc/cpuinfo
553 * siblings / cpu cores should give hyperthreading ratio
554 * otherwise fall back on sysconf */
555int UTIL_countPhysicalCores(void)
556{
557 static int numPhysicalCores = 0;
558
559 if (numPhysicalCores != 0) return numPhysicalCores;
560
561 numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
562 if (numPhysicalCores == -1) {
563 /* value not queryable, fall back on 1 */
564 return numPhysicalCores = 1;
565 }
566
567 /* try to determine if there's hyperthreading */
568 { FILE* const cpuinfo = fopen("/proc/cpuinfo", "r");
569#define BUF_SIZE 80
570 char buff[BUF_SIZE];
571
572 int siblings = 0;
573 int cpu_cores = 0;
574 int ratio = 1;
575
576 if (cpuinfo == NULL) {
577 /* fall back on the sysconf value */
578 return numPhysicalCores;
579 }
580
581 /* assume the cpu cores/siblings values will be constant across all
582 * present processors */
583 while (!feof(cpuinfo)) {
584 if (fgets(buff, BUF_SIZE, cpuinfo) != NULL) {
585 if (strncmp(buff, "siblings", 8) == 0) {
586 const char* const sep = strchr(buff, ':');
LeeYoung624c5caaf52019-07-29 17:05:50 +0800587 if (sep == NULL || *sep == '\0') {
Rohit Jain91b2fed2018-10-11 17:34:47 -0700588 /* formatting was broken? */
589 goto failed;
590 }
591
592 siblings = atoi(sep + 1);
593 }
594 if (strncmp(buff, "cpu cores", 9) == 0) {
595 const char* const sep = strchr(buff, ':');
LeeYoung624c5caaf52019-07-29 17:05:50 +0800596 if (sep == NULL || *sep == '\0') {
Rohit Jain91b2fed2018-10-11 17:34:47 -0700597 /* formatting was broken? */
598 goto failed;
599 }
600
601 cpu_cores = atoi(sep + 1);
602 }
603 } else if (ferror(cpuinfo)) {
604 /* fall back on the sysconf value */
605 goto failed;
606 }
607 }
608 if (siblings && cpu_cores) {
609 ratio = siblings / cpu_cores;
610 }
611failed:
612 fclose(cpuinfo);
613 return numPhysicalCores = numPhysicalCores / ratio;
614 }
615}
616
Conrad Meyerfe826372019-01-04 11:57:12 -0800617#elif defined(__FreeBSD__)
Rohit Jain91b2fed2018-10-11 17:34:47 -0700618
Conrad Meyerfe826372019-01-04 11:57:12 -0800619#include <sys/param.h>
620#include <sys/sysctl.h>
621
622/* Use physical core sysctl when available
623 * see: man 4 smp, man 3 sysctl */
624int UTIL_countPhysicalCores(void)
625{
626 static int numPhysicalCores = 0; /* freebsd sysctl is native int sized */
627 if (numPhysicalCores != 0) return numPhysicalCores;
628
629#if __FreeBSD_version >= 1300008
630 { size_t size = sizeof(numPhysicalCores);
631 int ret = sysctlbyname("kern.smp.cores", &numPhysicalCores, &size, NULL, 0);
632 if (ret == 0) return numPhysicalCores;
633 if (errno != ENOENT) {
634 perror("zstd: can't get number of physical cpus");
635 exit(1);
636 }
637 /* sysctl not present, fall through to older sysconf method */
638 }
639#endif
640
641 numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
642 if (numPhysicalCores == -1) {
643 /* value not queryable, fall back on 1 */
644 numPhysicalCores = 1;
645 }
646 return numPhysicalCores;
647}
648
649#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
650
651/* Use POSIX sysconf
652 * see: man 3 sysconf */
Rohit Jain91b2fed2018-10-11 17:34:47 -0700653int UTIL_countPhysicalCores(void)
654{
655 static int numPhysicalCores = 0;
656
657 if (numPhysicalCores != 0) return numPhysicalCores;
658
659 numPhysicalCores = (int)sysconf(_SC_NPROCESSORS_ONLN);
660 if (numPhysicalCores == -1) {
661 /* value not queryable, fall back on 1 */
662 return numPhysicalCores = 1;
663 }
664 return numPhysicalCores;
665}
666
667#else
668
669int UTIL_countPhysicalCores(void)
670{
671 /* assume 1 */
672 return 1;
673}
674
675#endif
676
Rohit Jainf881ee82018-10-11 12:52:19 -0700677#if defined (__cplusplus)
678}
679#endif