mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-04-26 21:45:41 -04:00
215 lines
6.2 KiB
C
215 lines
6.2 KiB
C
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sched.h>
|
|
|
|
#if CPUINFO_MOCK
|
|
#include <cpuinfo-mock.h>
|
|
#endif
|
|
#include <linux/api.h>
|
|
#include <cpuinfo/log.h>
|
|
|
|
|
|
/*
|
|
* Size, in chars, of the on-stack buffer used for parsing cpu lists.
|
|
* This is also the limit on the length of a single entry
|
|
* (<cpu-number> or <cpu-number-start>-<cpu-number-end>)
|
|
* in the cpu list.
|
|
*/
|
|
#define BUFFER_SIZE 256
|
|
|
|
|
|
/* Locale-independent */
|
|
inline static bool is_whitespace(char c) {
|
|
switch (c) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
case '\r':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline static const char* parse_number(const char* string, const char* end, uint32_t number_ptr[restrict static 1]) {
|
|
uint32_t number = 0;
|
|
while (string != end) {
|
|
const uint32_t digit = (uint32_t) (*string) - (uint32_t) '0';
|
|
if (digit >= 10) {
|
|
break;
|
|
}
|
|
number = number * UINT32_C(10) + digit;
|
|
string += 1;
|
|
}
|
|
*number_ptr = number;
|
|
return string;
|
|
}
|
|
|
|
inline static bool parse_entry(const char* entry_start, const char* entry_end, cpuinfo_cpulist_callback callback, void* context) {
|
|
/* Skip whitespace at the beginning of an entry */
|
|
for (; entry_start != entry_end; entry_start++) {
|
|
if (!is_whitespace(*entry_start)) {
|
|
break;
|
|
}
|
|
}
|
|
/* Skip whitespace at the end of an entry */
|
|
for (; entry_end != entry_start; entry_end--) {
|
|
if (!is_whitespace(entry_end[-1])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
const size_t entry_length = (size_t) (entry_end - entry_start);
|
|
if (entry_length == 0) {
|
|
cpuinfo_log_warning("unexpected zero-length cpu list entry ignored");
|
|
return false;
|
|
}
|
|
|
|
#if CPUINFO_LOG_DEBUG_PARSERS
|
|
cpuinfo_log_debug("parse cpu list entry \"%.*s\" (%zu chars)", (int) entry_length, entry_start, entry_length);
|
|
#endif
|
|
uint32_t first_cpu, last_cpu;
|
|
|
|
const char* number_end = parse_number(entry_start, entry_end, &first_cpu);
|
|
if (number_end == entry_start) {
|
|
/* Failed to parse the number; ignore the entry */
|
|
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
|
|
entry_start[0], (int) entry_length, entry_start);
|
|
return false;
|
|
} else if (number_end == entry_end) {
|
|
/* Completely parsed the entry */
|
|
#if CPUINFO_LOG_DEBUG_PARSERS
|
|
cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32,
|
|
first_cpu, first_cpu + 1);
|
|
#endif
|
|
return callback(first_cpu, first_cpu + 1, context);
|
|
}
|
|
|
|
/* Parse the second part of the entry */
|
|
if (*number_end != '-') {
|
|
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
|
|
*number_end, (int) entry_length, entry_start);
|
|
return false;
|
|
}
|
|
|
|
const char* number_start = number_end + 1;
|
|
number_end = parse_number(number_start, entry_end, &last_cpu);
|
|
if (number_end == number_start) {
|
|
/* Failed to parse the second number; ignore the entry */
|
|
cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
|
|
*number_start, (int) entry_length, entry_start);
|
|
return false;
|
|
}
|
|
|
|
if (number_end != entry_end) {
|
|
/* Partially parsed the entry; ignore unparsed characters and continue with the parsed part */
|
|
cpuinfo_log_warning("ignored invalid characters \"%.*s\" at the end of cpu list entry \"%.*s\"",
|
|
(int) (entry_end - number_end), number_start, (int) entry_length, entry_start);
|
|
}
|
|
|
|
if (last_cpu < first_cpu) {
|
|
cpuinfo_log_warning("ignored cpu list entry \"%.*s\": invalid range %"PRIu32"-%"PRIu32,
|
|
(int) entry_length, entry_start, first_cpu, last_cpu);
|
|
return false;
|
|
}
|
|
|
|
/* Parsed both parts of the entry; update CPU set */
|
|
#if CPUINFO_LOG_DEBUG_PARSERS
|
|
cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32,
|
|
first_cpu, last_cpu + 1);
|
|
#endif
|
|
return callback(first_cpu, last_cpu + 1, context);
|
|
}
|
|
|
|
bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback callback, void* context) {
|
|
bool status = true;
|
|
int file = -1;
|
|
char buffer[BUFFER_SIZE];
|
|
#if CPUINFO_LOG_DEBUG_PARSERS
|
|
cpuinfo_log_debug("parsing cpu list from file %s", filename);
|
|
#endif
|
|
|
|
#if CPUINFO_MOCK
|
|
file = cpuinfo_mock_open(filename, O_RDONLY);
|
|
#else
|
|
file = open(filename, O_RDONLY);
|
|
#endif
|
|
if (file == -1) {
|
|
cpuinfo_log_info("failed to open %s: %s", filename, strerror(errno));
|
|
status = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
size_t position = 0;
|
|
const char* buffer_end = &buffer[BUFFER_SIZE];
|
|
char* data_start = buffer;
|
|
ssize_t bytes_read;
|
|
do {
|
|
#if CPUINFO_MOCK
|
|
bytes_read = cpuinfo_mock_read(file, data_start, (size_t) (buffer_end - data_start));
|
|
#else
|
|
bytes_read = read(file, data_start, (size_t) (buffer_end - data_start));
|
|
#endif
|
|
if (bytes_read < 0) {
|
|
cpuinfo_log_info("failed to read file %s at position %zu: %s", filename, position, strerror(errno));
|
|
status = false;
|
|
goto cleanup;
|
|
}
|
|
|
|
position += (size_t) bytes_read;
|
|
const char* data_end = data_start + (size_t) bytes_read;
|
|
const char* entry_start = buffer;
|
|
|
|
if (bytes_read == 0) {
|
|
/* No more data in the file: process the remaining text in the buffer as a single entry */
|
|
const char* entry_end = data_end;
|
|
const bool entry_status = parse_entry(entry_start, entry_end, callback, context);
|
|
status &= entry_status;
|
|
} else {
|
|
const char* entry_end;
|
|
do {
|
|
/* Find the end of the entry, as indicated by a comma (',') */
|
|
for (entry_end = entry_start; entry_end != data_end; entry_end++) {
|
|
if (*entry_end == ',') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If we located separator at the end of the entry, parse it.
|
|
* Otherwise, there may be more data at the end; read the file once again.
|
|
*/
|
|
if (entry_end != data_end) {
|
|
const bool entry_status = parse_entry(entry_start, entry_end, callback, context);
|
|
status &= entry_status;
|
|
entry_start = entry_end + 1;
|
|
}
|
|
} while (entry_end != data_end);
|
|
|
|
/* Move remaining partial entry data at the end to the beginning of the buffer */
|
|
const size_t entry_length = (size_t) (entry_end - entry_start);
|
|
memmove(buffer, entry_start, entry_length);
|
|
data_start = &buffer[entry_length];
|
|
}
|
|
} while (bytes_read != 0);
|
|
|
|
cleanup:
|
|
if (file != -1) {
|
|
#if CPUINFO_MOCK
|
|
cpuinfo_mock_close(file);
|
|
#else
|
|
close(file);
|
|
#endif
|
|
file = -1;
|
|
}
|
|
return status;
|
|
}
|