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