static bool parse_line()

in src/arm/linux/cpuinfo.c [664:891]


static bool parse_line(
	const char* line_start,
	const char* line_end,
	struct proc_cpuinfo_parser_state state[restrict static 1],
	uint64_t line_number)
{
	/* Empty line. Skip. */
	if (line_start == line_end) {
		return true;
	}

	/* Search for ':' on the line. */
	const char* separator = line_start;
	for (; separator != line_end; separator++) {
		if (*separator == ':') {
			break;
		}
	}
	/* Skip line if no ':' separator was found. */
	if (separator == line_end) {
		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key/value separator ':' not found",
			(int) (line_end - line_start), line_start);
		return true;
	}

	/* Skip trailing spaces in key part. */
	const char* key_end = separator;
	for (; key_end != line_start; key_end--) {
		if (key_end[-1] != ' ' && key_end[-1] != '\t') {
			break;
		}
	}
	/* Skip line if key contains nothing but spaces. */
	if (key_end == line_start) {
		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: key contains only spaces",
			(int) (line_end - line_start), line_start);
		return true;
	}

	/* Skip leading spaces in value part. */
	const char* value_start = separator + 1;
	for (; value_start != line_end; value_start++) {
		if (*value_start != ' ') {
			break;
		}
	}
	/* Value part contains nothing but spaces. Skip line. */
	if (value_start == line_end) {
		cpuinfo_log_info("Line %.*s in /proc/cpuinfo is ignored: value contains only spaces",
			(int) (line_end - line_start), line_start);
		return true;
	}

	/* Skip trailing spaces in value part (if any) */
	const char* value_end = line_end;
	for (; value_end != value_start; value_end--) {
		if (value_end[-1] != ' ') {
			break;
		}
	}

	const uint32_t processor_index      = state->processor_index;
	const uint32_t max_processors_count = state->max_processors_count;
	struct cpuinfo_arm_linux_processor* processors = state->processors;
	struct cpuinfo_arm_linux_processor* processor  = &state->dummy_processor;
	if (processor_index < max_processors_count) {
		processor = &processors[processor_index];
	}

	const size_t key_length = key_end - line_start;
	switch (key_length) {
		case 6:
			if (memcmp(line_start, "Serial", key_length) == 0) {
				/* Usually contains just zeros, useless */
#if CPUINFO_ARCH_ARM
			} else if (memcmp(line_start, "I size", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"instruction cache size", &processor->proc_cpuinfo_cache.i_size,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SIZE);
			} else if (memcmp(line_start, "I sets", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"instruction cache sets", &processor->proc_cpuinfo_cache.i_sets,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_SETS);
			} else if (memcmp(line_start, "D size", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"data cache size", &processor->proc_cpuinfo_cache.d_size,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SIZE);
			} else if (memcmp(line_start, "D sets", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"data cache sets", &processor->proc_cpuinfo_cache.d_sets,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_SETS);
#endif /* CPUINFO_ARCH_ARM */
			} else {
				goto unknown;
			}
			break;
#if CPUINFO_ARCH_ARM
		case 7:
			if (memcmp(line_start, "I assoc", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"instruction cache associativity", &processor->proc_cpuinfo_cache.i_assoc,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_WAYS);
			} else if (memcmp(line_start, "D assoc", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"data cache associativity", &processor->proc_cpuinfo_cache.d_assoc,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_WAYS);
			} else {
				goto unknown;
			}
			break;
#endif /* CPUINFO_ARCH_ARM */
		case 8:
			if (memcmp(line_start, "CPU part", key_length) == 0) {
				parse_cpu_part(value_start, value_end, processor);
			} else if (memcmp(line_start, "Features", key_length) == 0) {
				parse_features(value_start, value_end, processor);
			} else if (memcmp(line_start, "BogoMIPS", key_length) == 0) {
				/* BogoMIPS is useless, don't parse */
			} else if (memcmp(line_start, "Hardware", key_length) == 0) {
				size_t value_length = value_end - value_start;
				if (value_length > CPUINFO_HARDWARE_VALUE_MAX) {
					cpuinfo_log_info(
						"length of Hardware value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
						(int) value_length, value_start, CPUINFO_HARDWARE_VALUE_MAX);
					value_length = CPUINFO_HARDWARE_VALUE_MAX;
				} else {
					state->hardware[value_length] = '\0';
				}
				memcpy(state->hardware, value_start, value_length);
				cpuinfo_log_debug("parsed /proc/cpuinfo Hardware = \"%.*s\"", (int) value_length, value_start);
			} else if (memcmp(line_start, "Revision", key_length) == 0) {
				size_t value_length = value_end - value_start;
				if (value_length > CPUINFO_REVISION_VALUE_MAX) {
					cpuinfo_log_info(
						"length of Revision value \"%.*s\" in /proc/cpuinfo exceeds limit (%d): truncating to the limit",
						(int) value_length, value_start, CPUINFO_REVISION_VALUE_MAX);
					value_length = CPUINFO_REVISION_VALUE_MAX;
				} else {
					state->revision[value_length] = '\0';
				}
				memcpy(state->revision, value_start, value_length);
				cpuinfo_log_debug("parsed /proc/cpuinfo Revision = \"%.*s\"", (int) value_length, value_start);
			} else {
				goto unknown;
			}
			break;
		case 9:
			if (memcmp(line_start, "processor", key_length) == 0) {
				const uint32_t new_processor_index = parse_processor_number(value_start, value_end);
				if (new_processor_index < processor_index) {
					/* Strange: decreasing processor number */
					cpuinfo_log_warning(
						"unexpectedly low processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
						new_processor_index, processor_index);
				} else if (new_processor_index > processor_index + 1) {
					/* Strange, but common: skipped processor $(processor_index + 1) */
					cpuinfo_log_info(
						"unexpectedly high processor number %"PRIu32" following processor %"PRIu32" in /proc/cpuinfo",
						new_processor_index, processor_index);
				}
				if (new_processor_index < max_processors_count) {
					/* Record that the processor was mentioned in /proc/cpuinfo */
					processors[new_processor_index].flags |= CPUINFO_ARM_LINUX_VALID_PROCESSOR;
				} else {
					/* Log and ignore processor */
					cpuinfo_log_warning("processor %"PRIu32" in /proc/cpuinfo is ignored: index exceeds system limit %"PRIu32,
						new_processor_index, max_processors_count - 1);
				}
				state->processor_index = new_processor_index;
				return true;
			} else if (memcmp(line_start, "Processor", key_length) == 0) {
				/* TODO: parse to fix misreported architecture, similar to Android's cpufeatures */
			} else {
				goto unknown;
			}
			break;
		case 11:
			if (memcmp(line_start, "CPU variant", key_length) == 0) {
				parse_cpu_variant(value_start, value_end, processor);
			} else {
				goto unknown;
			}
			break;
		case 12:
			if (memcmp(line_start, "CPU revision", key_length) == 0) {
				parse_cpu_revision(value_start, value_end, processor);
			} else {
				goto unknown;
			}
			break;
#if CPUINFO_ARCH_ARM
		case 13:
			if (memcmp(line_start, "I line length", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"instruction cache line size", &processor->proc_cpuinfo_cache.i_line_length,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_ICACHE_LINE);
			} else if (memcmp(line_start, "D line length", key_length) == 0) {
				parse_cache_number(value_start, value_end,
					"data cache line size", &processor->proc_cpuinfo_cache.d_line_length,
					&processor->flags, CPUINFO_ARM_LINUX_VALID_DCACHE_LINE);
			} else {
				goto unknown;
			}
			break;
#endif /* CPUINFO_ARCH_ARM */
		case 15:
			if (memcmp(line_start, "CPU implementer", key_length) == 0) {
				parse_cpu_implementer(value_start, value_end, processor);
			} else if (memcmp(line_start, "CPU implementor", key_length) == 0) {
				parse_cpu_implementer(value_start, value_end, processor);
			} else {
				goto unknown;
			}
			break;
		case 16:
			if (memcmp(line_start, "CPU architecture", key_length) == 0) {
				parse_cpu_architecture(value_start, value_end, processor);
			} else {
				goto unknown;
			}
			break;
		default:
		unknown:
			cpuinfo_log_debug("unknown /proc/cpuinfo key: %.*s", (int) key_length, line_start);

	}
	return true;
}