|
#include <stdbool.h> |
|
#include <stdint.h> |
|
#include <stddef.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
|
|
#include <cpuinfo.h> |
|
#include <cpuinfo/common.h> |
|
#include <x86/api.h> |
|
|
|
|
|
|
|
struct parser_state { |
|
|
|
|
|
|
|
|
|
char* context_model; |
|
|
|
|
|
|
|
|
|
char* context_upper_letter; |
|
|
|
|
|
|
|
|
|
char* context_dual; |
|
|
|
|
|
|
|
|
|
char* context_core; |
|
|
|
|
|
|
|
|
|
char* context_engineering; |
|
|
|
|
|
|
|
|
|
char* frequency_separator; |
|
|
|
bool frequency_token; |
|
|
|
bool xeon; |
|
|
|
bool parsed_model_number; |
|
|
|
bool engineering_sample; |
|
}; |
|
|
|
|
|
static void reset_context(struct parser_state* state) { |
|
state->context_model = NULL; |
|
state->context_upper_letter = NULL; |
|
state->context_dual = NULL; |
|
state->context_core = NULL; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline bool erase_matching(char* string, size_t length, const char* target) { |
|
const bool match = memcmp(string, target, length) == 0; |
|
if (match) { |
|
memset(string, ' ', length); |
|
} |
|
return match; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline bool is_upper_letter(char character) { |
|
return (uint32_t) (character - 'A') <= (uint32_t)('Z' - 'A'); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline bool is_digit(char character) { |
|
return (uint32_t) (character - '0') < UINT32_C(10); |
|
} |
|
|
|
static inline bool is_zero_number(const char* token_start, const char* token_end) { |
|
for (const char* char_ptr = token_start; char_ptr != token_end; char_ptr++) { |
|
if (*char_ptr != '0') { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
static inline bool is_space(const char* token_start, const char* token_end) { |
|
for (const char* char_ptr = token_start; char_ptr != token_end; char_ptr++) { |
|
if (*char_ptr != ' ') { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
static inline bool is_number(const char* token_start, const char* token_end) { |
|
for (const char* char_ptr = token_start; char_ptr != token_end; char_ptr++) { |
|
if (!is_digit(*char_ptr)) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
static inline bool is_model_number(const char* token_start, const char* token_end) { |
|
for (const char* char_ptr = token_start + 1; char_ptr < token_end; char_ptr++) { |
|
if (is_digit(char_ptr[-1]) && is_digit(char_ptr[0])) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
static inline bool is_frequency(const char* token_start, const char* token_end) { |
|
const size_t token_length = (size_t) (token_end - token_start); |
|
if (token_length > 3 && token_end[-2] == 'H' && token_end[-1] == 'z') { |
|
switch (token_end[-3]) { |
|
case 'K': |
|
case 'M': |
|
case 'G': |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
static inline char* move_token(const char* token_start, const char* token_end, char* output_ptr) { |
|
const size_t token_length = (size_t) (token_end - token_start); |
|
memmove(output_ptr, token_start, token_length); |
|
return output_ptr + token_length; |
|
} |
|
|
|
static bool transform_token(char* token_start, char* token_end, struct parser_state* state) { |
|
const struct parser_state previousState = *state; |
|
reset_context(state); |
|
|
|
size_t token_length = (size_t) (token_end - token_start); |
|
|
|
if (state->frequency_separator != NULL) { |
|
if (token_start > state->frequency_separator) { |
|
if (state->parsed_model_number) { |
|
memset(token_start, ' ', token_length); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (token_length > 2) { |
|
const char context_char = token_end[-3]; |
|
if (is_digit(context_char) || is_upper_letter(context_char)) { |
|
if (erase_matching(token_end - 2, 2, "tm")) { |
|
token_end -= 2; |
|
token_length -= 2; |
|
} |
|
} |
|
} |
|
if (token_length > 4) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, 4, "AMD-")) { |
|
token_start += 4; |
|
token_length -= 4; |
|
} |
|
} |
|
switch (token_length) { |
|
case 1: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (is_upper_letter(token_start[0])) { |
|
state->context_upper_letter = token_start; |
|
return true; |
|
} |
|
break; |
|
case 2: |
|
|
|
if (erase_matching(token_start, token_length, "w/")) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (token_start[0] == 'V' && is_digit(token_start[1])) { |
|
token_start[0] = 'v'; |
|
return true; |
|
} |
|
break; |
|
case 3: |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "CPU")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "SOC")) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "AMD")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "VIA")) { |
|
return true; |
|
} |
|
|
|
if (erase_matching(token_start, token_length, "IDT")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "MMX")) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "APU")) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
if (memcmp(token_start, "Eng", token_length) == 0) { |
|
state->context_engineering = token_start; |
|
} |
|
break; |
|
case 4: |
|
|
|
if (memcmp(token_start, "Dual", token_length) == 0) { |
|
state->context_dual = token_start; |
|
} |
|
|
|
if (memcmp(token_start, "Xeon", token_length) == 0) { |
|
state->xeon = true; |
|
} |
|
|
|
if (previousState.context_dual != NULL) { |
|
if (memcmp(token_start, "Core", token_length) == 0) { |
|
memset(previousState.context_dual, ' ', (size_t) (token_end - previousState.context_dual)); |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
} |
|
break; |
|
case 5: |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Intel")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Cyrix")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (memcmp(token_start, "Geode", token_length) == 0) { |
|
return false; |
|
} |
|
|
|
if (memcmp(token_start, "model", token_length) == 0) { |
|
state->context_model = token_start; |
|
return true; |
|
} |
|
break; |
|
case 6: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Radeon") || erase_matching(token_start, token_length, "RADEON")) { |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
if (previousState.context_core != NULL) { |
|
if (erase_matching(token_start, token_length, "Mobile")) { |
|
return true; |
|
} |
|
} |
|
|
|
if (erase_matching(token_start, token_length, "family")) { |
|
return true; |
|
} |
|
|
|
if (previousState.context_engineering != NULL) { |
|
if (memcmp(token_start, "Sample", token_length) == 0) { |
|
state->engineering_sample = true; |
|
return false; |
|
} |
|
} |
|
break; |
|
case 7: |
|
|
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Genuine")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "12-Core")) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "16-Core")) { |
|
return true; |
|
} |
|
|
|
if (previousState.context_model != NULL) { |
|
if (memcmp(token_start, "unknown", token_length) == 0) { |
|
memset(previousState.context_model, ' ', token_end - previousState.context_model); |
|
return true; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (previousState.context_engineering != NULL) { |
|
if (memcmp(token_start, "Sample,", token_length) == 0 || memcmp(token_start, "Sample:", token_length) == 0) { |
|
state->engineering_sample = true; |
|
return false; |
|
} |
|
} |
|
break; |
|
case 8: |
|
|
|
if (erase_matching(token_start, token_length, "QuadCore")) { |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
|
|
if (erase_matching(token_start, token_length, "Six-Core")) { |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
break; |
|
case 9: |
|
if (erase_matching(token_start, token_length, "Processor")) { |
|
return true; |
|
} |
|
if (erase_matching(token_start, token_length, "processor")) { |
|
return true; |
|
} |
|
|
|
if (erase_matching(token_start, token_length, "Dual-Core")) { |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Quad-Core")) { |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Transmeta")) { |
|
return true; |
|
} |
|
break; |
|
case 10: |
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Eight-Core")) { |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
break; |
|
case 11: |
|
|
|
|
|
|
|
|
|
|
|
if (erase_matching(token_start, token_length, "Triple-Core")) { |
|
state->context_core = token_end; |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
if (memcmp(token_start, "Engineering", token_length) == 0) { |
|
state->context_engineering = token_start; |
|
return true; |
|
} |
|
break; |
|
} |
|
if (is_zero_number(token_start, token_end)) { |
|
memset(token_start, ' ', token_length); |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (previousState.context_upper_letter != 0) { |
|
|
|
switch (token_length) { |
|
case 2: |
|
case 3: |
|
case 4: |
|
case 5: |
|
if (is_number(token_start, token_end)) { |
|
|
|
const char letter = *previousState.context_upper_letter; |
|
|
|
*previousState.context_upper_letter = ' '; |
|
|
|
move_token(token_start, token_end, token_start - 1); |
|
token_start -= 1; |
|
|
|
|
|
|
|
|
|
token_end[-1] = letter; |
|
} |
|
} |
|
} |
|
if (state->frequency_separator != NULL) { |
|
if (is_model_number(token_start, token_end)) { |
|
state->parsed_model_number = true; |
|
} |
|
} |
|
if (is_frequency(token_start, token_end)) { |
|
state->frequency_token = true; |
|
} |
|
return true; |
|
} |
|
|
|
uint32_t cpuinfo_x86_normalize_brand_string( |
|
const char raw_name[48], |
|
char normalized_name[48]) |
|
{ |
|
normalized_name[0] = '\0'; |
|
char name[48]; |
|
memcpy(name, raw_name, sizeof(name)); |
|
|
|
|
|
|
|
|
|
|
|
char* name_end = &name[48]; |
|
while (name_end[-1] == '\0') { |
|
|
|
|
|
|
|
|
|
if (--name_end == name) { |
|
|
|
return 0; |
|
} |
|
} |
|
|
|
struct parser_state parser_state = { 0 }; |
|
|
|
|
|
{ |
|
bool inside_parentheses = false; |
|
for (char* char_ptr = name; char_ptr != name_end; char_ptr++) { |
|
switch (*char_ptr) { |
|
case '(': |
|
inside_parentheses = true; |
|
*char_ptr = ' '; |
|
break; |
|
case ')': |
|
inside_parentheses = false; |
|
*char_ptr = ' '; |
|
break; |
|
case '@': |
|
parser_state.frequency_separator = char_ptr; |
|
case '\0': |
|
case '\t': |
|
*char_ptr = ' '; |
|
break; |
|
default: |
|
if (inside_parentheses) { |
|
*char_ptr = ' '; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
{ |
|
bool is_token = false; |
|
char* token_start = NULL; |
|
for (char* char_ptr = name; char_ptr != name_end; char_ptr++) { |
|
if (*char_ptr == ' ') { |
|
if (is_token) { |
|
is_token = false; |
|
if (!transform_token(token_start, char_ptr, &parser_state)) { |
|
name_end = char_ptr; |
|
break; |
|
} |
|
} |
|
} else { |
|
if (!is_token) { |
|
is_token = true; |
|
token_start = char_ptr; |
|
} |
|
} |
|
} |
|
if (is_token) { |
|
transform_token(token_start, name_end, &parser_state); |
|
} |
|
} |
|
|
|
|
|
if (parser_state.engineering_sample) { |
|
return 0; |
|
} |
|
|
|
|
|
if (parser_state.frequency_separator != NULL) { |
|
if (is_space(name, parser_state.frequency_separator)) { |
|
|
|
return 0; |
|
} |
|
} |
|
|
|
|
|
{ |
|
char* output_ptr = normalized_name; |
|
char* token_start = NULL; |
|
bool is_token = false; |
|
bool previous_token_ends_with_dash = true; |
|
bool current_token_starts_with_dash = false; |
|
uint32_t token_count = 1; |
|
for (char* char_ptr = name; char_ptr != name_end; char_ptr++) { |
|
const char character = *char_ptr; |
|
if (character == ' ') { |
|
if (is_token) { |
|
is_token = false; |
|
if (!current_token_starts_with_dash && !previous_token_ends_with_dash) { |
|
token_count += 1; |
|
*output_ptr++ = ' '; |
|
} |
|
output_ptr = move_token(token_start, char_ptr, output_ptr); |
|
|
|
previous_token_ends_with_dash = (char_ptr[-1] == '-'); |
|
} |
|
} else { |
|
if (!is_token) { |
|
is_token = true; |
|
token_start = char_ptr; |
|
current_token_starts_with_dash = (character == '-'); |
|
} |
|
} |
|
} |
|
if (is_token) { |
|
if (!current_token_starts_with_dash && !previous_token_ends_with_dash) { |
|
token_count += 1; |
|
*output_ptr++ = ' '; |
|
} |
|
output_ptr = move_token(token_start, name_end, output_ptr); |
|
} |
|
if (parser_state.frequency_token && token_count <= 1) { |
|
|
|
normalized_name[0] = '\0'; |
|
return 0; |
|
} |
|
if (output_ptr < &normalized_name[48]) { |
|
*output_ptr = '\0'; |
|
} else { |
|
normalized_name[47] = '\0'; |
|
} |
|
return (uint32_t) (output_ptr - normalized_name); |
|
} |
|
} |
|
|
|
static const char* vendor_string_map[] = { |
|
[cpuinfo_vendor_intel] = "Intel", |
|
[cpuinfo_vendor_amd] = "AMD", |
|
[cpuinfo_vendor_via] = "VIA", |
|
[cpuinfo_vendor_hygon] = "Hygon", |
|
[cpuinfo_vendor_rdc] = "RDC", |
|
[cpuinfo_vendor_dmp] = "DM&P", |
|
[cpuinfo_vendor_transmeta] = "Transmeta", |
|
[cpuinfo_vendor_cyrix] = "Cyrix", |
|
[cpuinfo_vendor_rise] = "Rise", |
|
[cpuinfo_vendor_nsc] = "NSC", |
|
[cpuinfo_vendor_sis] = "SiS", |
|
[cpuinfo_vendor_nexgen] = "NexGen", |
|
[cpuinfo_vendor_umc] = "UMC", |
|
}; |
|
|
|
uint32_t cpuinfo_x86_format_package_name( |
|
enum cpuinfo_vendor vendor, |
|
const char normalized_brand_string[48], |
|
char package_name[CPUINFO_PACKAGE_NAME_MAX]) |
|
{ |
|
if (normalized_brand_string[0] == '\0') { |
|
package_name[0] = '\0'; |
|
return 0; |
|
} |
|
|
|
const char* vendor_string = NULL; |
|
if ((uint32_t) vendor < (uint32_t) CPUINFO_COUNT_OF(vendor_string_map)) { |
|
vendor_string = vendor_string_map[(uint32_t) vendor]; |
|
} |
|
if (vendor_string == NULL) { |
|
strncpy(package_name, normalized_brand_string, CPUINFO_PACKAGE_NAME_MAX); |
|
package_name[CPUINFO_PACKAGE_NAME_MAX - 1] = '\0'; |
|
return 0; |
|
} else { |
|
snprintf(package_name, CPUINFO_PACKAGE_NAME_MAX, |
|
"%s %s", vendor_string, normalized_brand_string); |
|
return (uint32_t) strlen(vendor_string) + 1; |
|
} |
|
} |
|
|