mirror of
https://github.com/google/cpu_features.git
synced 2025-07-01 05:11:15 +02:00
Adding code. Closes #0.
This commit is contained in:
32
src/cpuid_x86_clang.c
Normal file
32
src/cpuid_x86_clang.c
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/cpuid_x86.h"
|
||||
|
||||
#if defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_CLANG)
|
||||
#include <cpuid.h>
|
||||
|
||||
Leaf CpuId(uint32_t leaf_id) {
|
||||
Leaf leaf;
|
||||
__cpuid_count(leaf_id, 0, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
|
||||
return leaf;
|
||||
}
|
||||
|
||||
uint32_t GetXCR0Eax(void) {
|
||||
uint32_t eax, edx;
|
||||
__asm("XGETBV" : "=a"(eax), "=d"(edx) : "c"(0));
|
||||
return eax;
|
||||
}
|
||||
|
||||
#endif // defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_CLANG)
|
32
src/cpuid_x86_gcc.c
Normal file
32
src/cpuid_x86_gcc.c
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/cpuid_x86.h"
|
||||
|
||||
#if defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_GCC)
|
||||
#include <cpuid.h>
|
||||
|
||||
Leaf CpuId(uint32_t leaf_id) {
|
||||
Leaf leaf;
|
||||
__cpuid(leaf_id, leaf.eax, leaf.ebx, leaf.ecx, leaf.edx);
|
||||
return leaf;
|
||||
}
|
||||
|
||||
uint32_t GetXCR0Eax(void) {
|
||||
uint32_t eax, edx;
|
||||
__asm("XGETBV" : "=a"(eax), "=d"(edx) : "c"(0));
|
||||
return eax;
|
||||
}
|
||||
|
||||
#endif // defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_GCC)
|
34
src/cpuid_x86_msvc.c
Normal file
34
src/cpuid_x86_msvc.c
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/cpuid_x86.h"
|
||||
|
||||
#if defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_MSC)
|
||||
#include <immintrin.h>
|
||||
#include <intrin.h> // For __cpuidex()
|
||||
|
||||
Leaf CpuId(uint32_t leaf_id) {
|
||||
Leaf leaf;
|
||||
int data[4];
|
||||
__cpuid(data, leaf_id);
|
||||
leaf.eax = data[0];
|
||||
leaf.ebx = data[1];
|
||||
leaf.ecx = data[2];
|
||||
leaf.edx = data[3];
|
||||
return leaf;
|
||||
}
|
||||
|
||||
uint32_t GetXCR0Eax(void) { return _xgetbv(0); }
|
||||
|
||||
#endif // defined(CPU_FEATURES_ARCH_X86) && defined(CPU_FEATURES_COMPILER_MSC)
|
140
src/cpuinfo_aarch64.c
Normal file
140
src/cpuinfo_aarch64.c
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "cpuinfo_aarch64.h"
|
||||
|
||||
#include "internal/filesystem.h"
|
||||
#include "internal/hwcaps.h"
|
||||
#include "internal/linux_features_aggregator.h"
|
||||
#include "internal/stack_line_reader.h"
|
||||
#include "internal/string_view.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
DECLARE_SETTER(Aarch64Features, fp)
|
||||
DECLARE_SETTER(Aarch64Features, asimd)
|
||||
DECLARE_SETTER(Aarch64Features, aes)
|
||||
DECLARE_SETTER(Aarch64Features, pmull)
|
||||
DECLARE_SETTER(Aarch64Features, sha1)
|
||||
DECLARE_SETTER(Aarch64Features, sha2)
|
||||
DECLARE_SETTER(Aarch64Features, crc32)
|
||||
|
||||
static const CapabilityConfig kConfigs[] = {
|
||||
{{AARCH64_HWCAP_FP, 0}, "fp", &set_fp}, //
|
||||
{{AARCH64_HWCAP_ASIMD, 0}, "asimd", &set_asimd}, //
|
||||
{{AARCH64_HWCAP_AES, 0}, "aes", &set_aes}, //
|
||||
{{AARCH64_HWCAP_PMULL, 0}, "pmull", &set_pmull}, //
|
||||
{{AARCH64_HWCAP_SHA1, 0}, "sha1", &set_sha1}, //
|
||||
{{AARCH64_HWCAP_SHA2, 0}, "sha2", &set_sha2}, //
|
||||
{{AARCH64_HWCAP_CRC32, 0}, "crc32", &set_crc32}, //
|
||||
};
|
||||
|
||||
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
|
||||
|
||||
static bool HandleAarch64Line(const LineResult result,
|
||||
Aarch64Info* const info) {
|
||||
StringView line = result.line;
|
||||
StringView key, value;
|
||||
if (GetAttributeKeyValue(line, &key, &value)) {
|
||||
if (IsEquals(key, str("Features"))) {
|
||||
SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
|
||||
} else if (IsEquals(key, str("CPU implementer"))) {
|
||||
info->implementer = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU variant"))) {
|
||||
info->variant = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU part"))) {
|
||||
info->part = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU revision"))) {
|
||||
info->revision = ParsePositiveNumber(value);
|
||||
}
|
||||
}
|
||||
return !result.eof;
|
||||
}
|
||||
|
||||
static void FillProcCpuInfoData(Aarch64Info* const info) {
|
||||
const int fd = OpenFile("/proc/cpuinfo");
|
||||
if (fd >= 0) {
|
||||
StackLineReader reader;
|
||||
StackLineReader_Initialize(&reader, fd);
|
||||
for (;;) {
|
||||
if (!HandleAarch64Line(StackLineReader_NextLine(&reader), info)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseFile(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static const Aarch64Info kEmptyAarch64Info;
|
||||
|
||||
Aarch64Info GetAarch64Info(void) {
|
||||
// capabilities are fetched from both getauxval and /proc/cpuinfo so we can
|
||||
// have some information if the executable is sandboxed (aka no access to
|
||||
// /proc/cpuinfo).
|
||||
Aarch64Info info = kEmptyAarch64Info;
|
||||
|
||||
FillProcCpuInfoData(&info);
|
||||
OverrideFromHwCaps(kConfigsSize, kConfigs, GetHardwareCapabilities(),
|
||||
&info.features);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Introspection functions
|
||||
|
||||
int GetAarch64FeaturesEnumValue(const Aarch64Features* features,
|
||||
Aarch64FeaturesEnum value) {
|
||||
switch (value) {
|
||||
case AARCH64_FP:
|
||||
return features->fp;
|
||||
case AARCH64_ASIMD:
|
||||
return features->asimd;
|
||||
case AARCH64_AES:
|
||||
return features->aes;
|
||||
case AARCH64_PMULL:
|
||||
return features->pmull;
|
||||
case AARCH64_SHA1:
|
||||
return features->sha1;
|
||||
case AARCH64_SHA2:
|
||||
return features->sha2;
|
||||
case AARCH64_CRC32:
|
||||
return features->crc32;
|
||||
case AARCH64_LAST_:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetAarch64FeaturesEnumName(Aarch64FeaturesEnum value) {
|
||||
switch (value) {
|
||||
case AARCH64_FP:
|
||||
return "fp";
|
||||
case AARCH64_ASIMD:
|
||||
return "asimd";
|
||||
case AARCH64_AES:
|
||||
return "aes";
|
||||
case AARCH64_PMULL:
|
||||
return "pmull";
|
||||
case AARCH64_SHA1:
|
||||
return "sha1";
|
||||
case AARCH64_SHA2:
|
||||
return "sha2";
|
||||
case AARCH64_CRC32:
|
||||
return "crc32";
|
||||
case AARCH64_LAST_:
|
||||
break;
|
||||
}
|
||||
return "unknown feature";
|
||||
}
|
255
src/cpuinfo_arm.c
Normal file
255
src/cpuinfo_arm.c
Normal file
@ -0,0 +1,255 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "cpuinfo_arm.h"
|
||||
|
||||
#include "internal/bit_utils.h"
|
||||
#include "internal/filesystem.h"
|
||||
#include "internal/hwcaps.h"
|
||||
#include "internal/linux_features_aggregator.h"
|
||||
#include "internal/stack_line_reader.h"
|
||||
#include "internal/string_view.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
DECLARE_SETTER(ArmFeatures, vfp)
|
||||
DECLARE_SETTER(ArmFeatures, iwmmxt)
|
||||
DECLARE_SETTER(ArmFeatures, neon)
|
||||
DECLARE_SETTER(ArmFeatures, vfpv3)
|
||||
DECLARE_SETTER(ArmFeatures, vfpv3d16)
|
||||
DECLARE_SETTER(ArmFeatures, vfpv4)
|
||||
DECLARE_SETTER(ArmFeatures, idiva)
|
||||
DECLARE_SETTER(ArmFeatures, idivt)
|
||||
DECLARE_SETTER(ArmFeatures, aes)
|
||||
DECLARE_SETTER(ArmFeatures, pmull)
|
||||
DECLARE_SETTER(ArmFeatures, sha1)
|
||||
DECLARE_SETTER(ArmFeatures, sha2)
|
||||
DECLARE_SETTER(ArmFeatures, crc32)
|
||||
|
||||
static const CapabilityConfig kConfigs[] = {
|
||||
{{ARM_HWCAP_VFP, 0}, "vfp", &set_vfp}, //
|
||||
{{ARM_HWCAP_IWMMXT, 0}, "iwmmxt", &set_iwmmxt}, //
|
||||
{{ARM_HWCAP_NEON, 0}, "neon", &set_neon}, //
|
||||
{{ARM_HWCAP_VFPV3, 0}, "vfpv3", &set_vfpv3}, //
|
||||
{{ARM_HWCAP_VFPV3D16, 0}, "vfpv3d16", &set_vfpv3d16}, //
|
||||
{{ARM_HWCAP_VFPV4, 0}, "vfpv4", &set_vfpv4}, //
|
||||
{{ARM_HWCAP_IDIVA, 0}, "idiva", &set_idiva}, //
|
||||
{{ARM_HWCAP_IDIVT, 0}, "idivt", &set_idivt}, //
|
||||
{{0, ARM_HWCAP2_AES}, "aes", &set_aes}, //
|
||||
{{0, ARM_HWCAP2_PMULL}, "pmull", &set_pmull}, //
|
||||
{{0, ARM_HWCAP2_SHA1}, "sha1", &set_sha1}, //
|
||||
{{0, ARM_HWCAP2_SHA2}, "sha2", &set_sha2}, //
|
||||
{{0, ARM_HWCAP2_CRC32}, "crc32", &set_crc32}, //
|
||||
};
|
||||
|
||||
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
|
||||
|
||||
typedef struct {
|
||||
bool processor_reports_armv6;
|
||||
bool hardware_reports_goldfish;
|
||||
} ProcCpuInfoData;
|
||||
|
||||
static int IndexOfNonDigit(StringView str) {
|
||||
size_t index = 0;
|
||||
while (str.size && isdigit(Front(str))) {
|
||||
str = PopFront(str, 1);
|
||||
++index;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static bool HandleArmLine(const LineResult result, ArmInfo* const info,
|
||||
ProcCpuInfoData* const proc_info) {
|
||||
StringView line = result.line;
|
||||
StringView key, value;
|
||||
if (GetAttributeKeyValue(line, &key, &value)) {
|
||||
if (IsEquals(key, str("Features"))) {
|
||||
SetFromFlags(kConfigsSize, kConfigs, value, &info->features);
|
||||
} else if (IsEquals(key, str("CPU implementer"))) {
|
||||
info->implementer = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU variant"))) {
|
||||
info->variant = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU part"))) {
|
||||
info->part = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU revision"))) {
|
||||
info->revision = ParsePositiveNumber(value);
|
||||
} else if (IsEquals(key, str("CPU architecture"))) {
|
||||
// CPU architecture is a number that may be followed by letters. e.g.
|
||||
// "6TEJ", "7".
|
||||
const StringView digits = KeepFront(value, IndexOfNonDigit(value));
|
||||
info->architecture = ParsePositiveNumber(digits);
|
||||
} else if (IsEquals(key, str("Processor"))) {
|
||||
proc_info->processor_reports_armv6 = IndexOf(value, str("(v6l)")) >= 0;
|
||||
} else if (IsEquals(key, str("Hardware"))) {
|
||||
proc_info->hardware_reports_goldfish = IsEquals(value, str("Goldfish"));
|
||||
}
|
||||
}
|
||||
return !result.eof;
|
||||
}
|
||||
|
||||
static uint32_t GetCpuId(const ArmInfo* const info) {
|
||||
return (ExtractBitRange(info->implementer, 7, 0) << 24) |
|
||||
(ExtractBitRange(info->variant, 3, 0) << 20) |
|
||||
(ExtractBitRange(info->part, 11, 0) << 4) |
|
||||
(ExtractBitRange(info->revision, 3, 0) << 0);
|
||||
}
|
||||
|
||||
static void FixErrors(ArmInfo* const info,
|
||||
ProcCpuInfoData* const proc_cpu_info_data) {
|
||||
// Fixing Samsung kernel reporting invalid cpu architecture.
|
||||
// http://code.google.com/p/android/issues/detail?id=10812
|
||||
if (proc_cpu_info_data->processor_reports_armv6 && info->architecture >= 7) {
|
||||
info->architecture = 6;
|
||||
}
|
||||
|
||||
// Handle kernel configuration bugs that prevent the correct reporting of CPU
|
||||
// features.
|
||||
switch (GetCpuId(info)) {
|
||||
case 0x4100C080:
|
||||
// Special case: The emulator-specific Android 4.2 kernel fails to report
|
||||
// support for the 32-bit ARM IDIV instruction. Technically, this is a
|
||||
// feature of the virtual CPU implemented by the emulator. Note that it
|
||||
// could also support Thumb IDIV in the future, and this will have to be
|
||||
// slightly updated.
|
||||
if (info->architecture >= 7 &&
|
||||
proc_cpu_info_data->hardware_reports_goldfish) {
|
||||
info->features.idiva = true;
|
||||
}
|
||||
break;
|
||||
case 0x511004D0:
|
||||
// https://crbug.com/341598.
|
||||
info->features.neon = false;
|
||||
break;
|
||||
case 0x510006F2:
|
||||
case 0x510006F3:
|
||||
// The Nexus 4 (Qualcomm Krait) kernel configuration forgets to report
|
||||
// IDIV support.
|
||||
info->features.idiva = true;
|
||||
info->features.idivt = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// Propagate cpu features.
|
||||
if (info->features.vfpv4) info->features.vfpv3 = true;
|
||||
if (info->features.neon) info->features.vfpv3 = true;
|
||||
if (info->features.vfpv3) info->features.vfp = true;
|
||||
}
|
||||
|
||||
static void FillProcCpuInfoData(ArmInfo* const info,
|
||||
ProcCpuInfoData* proc_cpu_info_data) {
|
||||
const int fd = OpenFile("/proc/cpuinfo");
|
||||
if (fd >= 0) {
|
||||
StackLineReader reader;
|
||||
StackLineReader_Initialize(&reader, fd);
|
||||
for (;;) {
|
||||
if (!HandleArmLine(StackLineReader_NextLine(&reader), info,
|
||||
proc_cpu_info_data)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseFile(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static const ArmInfo kEmptyArmInfo;
|
||||
|
||||
static const ProcCpuInfoData kEmptyProcCpuInfoData;
|
||||
|
||||
ArmInfo GetArmInfo(void) {
|
||||
// capabilities are fetched from both getauxval and /proc/cpuinfo so we can
|
||||
// have some information if the executable is sandboxed (aka no access to
|
||||
// /proc/cpuinfo).
|
||||
ArmInfo info = kEmptyArmInfo;
|
||||
ProcCpuInfoData proc_cpu_info_data = kEmptyProcCpuInfoData;
|
||||
|
||||
FillProcCpuInfoData(&info, &proc_cpu_info_data);
|
||||
OverrideFromHwCaps(kConfigsSize, kConfigs, GetHardwareCapabilities(),
|
||||
&info.features);
|
||||
|
||||
FixErrors(&info, &proc_cpu_info_data);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Introspection functions
|
||||
|
||||
int GetArmFeaturesEnumValue(const ArmFeatures* features,
|
||||
ArmFeaturesEnum value) {
|
||||
switch (value) {
|
||||
case ARM_VFP:
|
||||
return features->vfp;
|
||||
case ARM_IWMMXT:
|
||||
return features->iwmmxt;
|
||||
case ARM_NEON:
|
||||
return features->neon;
|
||||
case ARM_VFPV3:
|
||||
return features->vfpv3;
|
||||
case ARM_VFPV3D16:
|
||||
return features->vfpv3d16;
|
||||
case ARM_VFPV4:
|
||||
return features->vfpv4;
|
||||
case ARM_IDIVA:
|
||||
return features->idiva;
|
||||
case ARM_IDIVT:
|
||||
return features->idivt;
|
||||
case ARM_AES:
|
||||
return features->aes;
|
||||
case ARM_PMULL:
|
||||
return features->pmull;
|
||||
case ARM_SHA1:
|
||||
return features->sha1;
|
||||
case ARM_SHA2:
|
||||
return features->sha2;
|
||||
case ARM_CRC32:
|
||||
return features->crc32;
|
||||
case ARM_LAST_:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetArmFeaturesEnumName(ArmFeaturesEnum value) {
|
||||
switch (value) {
|
||||
case ARM_VFP:
|
||||
return "vfp";
|
||||
case ARM_IWMMXT:
|
||||
return "iwmmxt";
|
||||
case ARM_NEON:
|
||||
return "neon";
|
||||
case ARM_VFPV3:
|
||||
return "vfpv3";
|
||||
case ARM_VFPV3D16:
|
||||
return "vfpv3d16";
|
||||
case ARM_VFPV4:
|
||||
return "vfpv4";
|
||||
case ARM_IDIVA:
|
||||
return "idiva";
|
||||
case ARM_IDIVT:
|
||||
return "idivt";
|
||||
case ARM_AES:
|
||||
return "aes";
|
||||
case ARM_PMULL:
|
||||
return "pmull";
|
||||
case ARM_SHA1:
|
||||
return "sha1";
|
||||
case ARM_SHA2:
|
||||
return "sha2";
|
||||
case ARM_CRC32:
|
||||
return "crc32";
|
||||
case ARM_LAST_:
|
||||
break;
|
||||
}
|
||||
return "unknown feature";
|
||||
}
|
97
src/cpuinfo_mips.c
Normal file
97
src/cpuinfo_mips.c
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "cpuinfo_mips.h"
|
||||
|
||||
#include "internal/filesystem.h"
|
||||
#include "internal/linux_features_aggregator.h"
|
||||
#include "internal/stack_line_reader.h"
|
||||
#include "internal/string_view.h"
|
||||
|
||||
DECLARE_SETTER(MipsFeatures, msa)
|
||||
DECLARE_SETTER(MipsFeatures, eva)
|
||||
|
||||
static const CapabilityConfig kConfigs[] = {
|
||||
{{MIPS_HWCAP_MSA, 0}, "msa", &set_msa}, //
|
||||
{{MIPS_HWCAP_EVA, 0}, "eva", &set_eva}, //
|
||||
};
|
||||
static const size_t kConfigsSize = sizeof(kConfigs) / sizeof(CapabilityConfig);
|
||||
|
||||
static bool HandleMipsLine(const LineResult result,
|
||||
MipsFeatures* const features) {
|
||||
StringView key, value;
|
||||
// See tests for an example.
|
||||
if (GetAttributeKeyValue(result.line, &key, &value)) {
|
||||
if (IsEquals(key, str("ASEs implemented"))) {
|
||||
SetFromFlags(kConfigsSize, kConfigs, value, features);
|
||||
}
|
||||
}
|
||||
return !result.eof;
|
||||
}
|
||||
|
||||
static void FillProcCpuInfoData(MipsFeatures* const features) {
|
||||
const int fd = OpenFile("/proc/cpuinfo");
|
||||
if (fd >= 0) {
|
||||
StackLineReader reader;
|
||||
StackLineReader_Initialize(&reader, fd);
|
||||
for (;;) {
|
||||
if (!HandleMipsLine(StackLineReader_NextLine(&reader), features)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseFile(fd);
|
||||
}
|
||||
}
|
||||
|
||||
static const MipsInfo kEmptyMipsInfo;
|
||||
|
||||
MipsInfo GetMipsInfo(void) {
|
||||
// capabilities are fetched from both getauxval and /proc/cpuinfo so we can
|
||||
// have some information if the executable is sandboxed (aka no access to
|
||||
// /proc/cpuinfo).
|
||||
MipsInfo info = kEmptyMipsInfo;
|
||||
|
||||
FillProcCpuInfoData(&info.features);
|
||||
OverrideFromHwCaps(kConfigsSize, kConfigs, GetHardwareCapabilities(),
|
||||
&info.features);
|
||||
return info;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Introspection functions
|
||||
|
||||
int GetMipsFeaturesEnumValue(const MipsFeatures* features,
|
||||
MipsFeaturesEnum value) {
|
||||
switch (value) {
|
||||
case MIPS_MSA:
|
||||
return features->msa;
|
||||
case MIPS_EVA:
|
||||
return features->eva;
|
||||
case MIPS_LAST_:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetMipsFeaturesEnumName(MipsFeaturesEnum value) {
|
||||
switch (value) {
|
||||
case MIPS_MSA:
|
||||
return "msa";
|
||||
case MIPS_EVA:
|
||||
return "eva";
|
||||
case MIPS_LAST_:
|
||||
break;
|
||||
}
|
||||
return "unknown feature";
|
||||
}
|
432
src/cpuinfo_x86.c
Normal file
432
src/cpuinfo_x86.c
Normal file
@ -0,0 +1,432 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "cpuinfo_x86.h"
|
||||
#include "internal/bit_utils.h"
|
||||
#include "internal/cpuid_x86.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
static const Leaf kEmptyLeaf;
|
||||
|
||||
static Leaf SafeCpuId(uint32_t max_cpuid_leaf, uint32_t leaf_id) {
|
||||
if (leaf_id <= max_cpuid_leaf) {
|
||||
return CpuId(leaf_id);
|
||||
} else {
|
||||
return kEmptyLeaf;
|
||||
}
|
||||
}
|
||||
|
||||
#define MASK_XMM 0x2
|
||||
#define MASK_YMM 0x4
|
||||
#define MASK_MASKREG 0x20
|
||||
#define MASK_ZMM0_15 0x40
|
||||
#define MASK_ZMM16_31 0x80
|
||||
|
||||
static bool HasMask(uint32_t value, uint32_t mask) {
|
||||
return (value & mask) == mask;
|
||||
}
|
||||
|
||||
// Checks that operating system saves and restores xmm registers during context
|
||||
// switches.
|
||||
static bool HasXmmOsXSave(uint32_t xcr0_eax) {
|
||||
return HasMask(xcr0_eax, MASK_XMM);
|
||||
}
|
||||
|
||||
// Checks that operating system saves and restores ymm registers during context
|
||||
// switches.
|
||||
static bool HasYmmOsXSave(uint32_t xcr0_eax) {
|
||||
return HasMask(xcr0_eax, MASK_XMM | MASK_YMM);
|
||||
}
|
||||
|
||||
// Checks that operating system saves and restores zmm registers during context
|
||||
// switches.
|
||||
static bool HasZmmOsXSave(uint32_t xcr0_eax) {
|
||||
return HasMask(xcr0_eax, MASK_XMM | MASK_YMM | MASK_MASKREG | MASK_ZMM0_15 |
|
||||
MASK_ZMM16_31);
|
||||
}
|
||||
|
||||
static void SetVendor(const Leaf leaf, char* const vendor) {
|
||||
*(uint32_t*)(vendor) = leaf.ebx;
|
||||
*(uint32_t*)(vendor + 4) = leaf.edx;
|
||||
*(uint32_t*)(vendor + 8) = leaf.ecx;
|
||||
vendor[12] = '\0';
|
||||
}
|
||||
|
||||
static int IsVendor(const Leaf leaf, const char* const name) {
|
||||
const uint32_t ebx = *(const uint32_t*)(name);
|
||||
const uint32_t edx = *(const uint32_t*)(name + 4);
|
||||
const uint32_t ecx = *(const uint32_t*)(name + 8);
|
||||
return leaf.ebx == ebx && leaf.ecx == ecx && leaf.edx == edx;
|
||||
}
|
||||
|
||||
// Reference https://en.wikipedia.org/wiki/CPUID.
|
||||
static void ParseCpuId(const uint32_t max_cpuid_leaf, X86Info* info) {
|
||||
const Leaf leaf_1 = SafeCpuId(max_cpuid_leaf, 1);
|
||||
const Leaf leaf_7 = SafeCpuId(max_cpuid_leaf, 7);
|
||||
|
||||
const bool have_xsave = IsBitSet(leaf_1.ecx, 26);
|
||||
const bool have_osxsave = IsBitSet(leaf_1.ecx, 27);
|
||||
const uint32_t xcr0_eax = (have_xsave && have_osxsave) ? GetXCR0Eax() : 0;
|
||||
const bool have_sse_os_support = HasXmmOsXSave(xcr0_eax);
|
||||
const bool have_avx_os_support = HasYmmOsXSave(xcr0_eax);
|
||||
const bool have_avx512_os_support = HasZmmOsXSave(xcr0_eax);
|
||||
|
||||
const uint32_t family = ExtractBitRange(leaf_1.eax, 11, 8);
|
||||
const uint32_t extended_family = ExtractBitRange(leaf_1.eax, 27, 20);
|
||||
const uint32_t model = ExtractBitRange(leaf_1.eax, 7, 4);
|
||||
const uint32_t extended_model = ExtractBitRange(leaf_1.eax, 19, 16);
|
||||
|
||||
X86Features* const features = &info->features;
|
||||
|
||||
info->family = extended_family + family;
|
||||
info->model = (extended_model << 4) + model;
|
||||
info->stepping = ExtractBitRange(leaf_1.eax, 3, 0);
|
||||
|
||||
features->aes = IsBitSet(leaf_1.ecx, 25);
|
||||
features->erms = IsBitSet(leaf_7.ebx, 9);
|
||||
features->f16c = IsBitSet(leaf_1.ecx, 29);
|
||||
features->bmi1 = IsBitSet(leaf_7.ebx, 3);
|
||||
features->bmi2 = IsBitSet(leaf_7.ebx, 8);
|
||||
features->vpclmulqdq = IsBitSet(leaf_7.ecx, 10);
|
||||
|
||||
if (have_sse_os_support) {
|
||||
features->ssse3 = IsBitSet(leaf_1.ecx, 9);
|
||||
features->sse4_1 = IsBitSet(leaf_1.ecx, 19);
|
||||
features->sse4_2 = IsBitSet(leaf_1.ecx, 20);
|
||||
}
|
||||
|
||||
if (have_avx_os_support) {
|
||||
features->fma3 = IsBitSet(leaf_1.ecx, 12);
|
||||
features->avx = IsBitSet(leaf_1.ecx, 28);
|
||||
features->avx2 = IsBitSet(leaf_7.ebx, 5);
|
||||
}
|
||||
|
||||
if (have_avx512_os_support) {
|
||||
features->avx512f = IsBitSet(leaf_7.ebx, 16);
|
||||
features->avx512cd = IsBitSet(leaf_7.ebx, 28);
|
||||
features->avx512er = IsBitSet(leaf_7.ebx, 27);
|
||||
features->avx512pf = IsBitSet(leaf_7.ebx, 26);
|
||||
features->avx512bw = IsBitSet(leaf_7.ebx, 30);
|
||||
features->avx512dq = IsBitSet(leaf_7.ebx, 17);
|
||||
features->avx512vl = IsBitSet(leaf_7.ebx, 31);
|
||||
features->avx512ifma = IsBitSet(leaf_7.ebx, 21);
|
||||
features->avx512vbmi = IsBitSet(leaf_7.ecx, 1);
|
||||
features->avx512vbmi2 = IsBitSet(leaf_7.ecx, 6);
|
||||
features->avx512vnni = IsBitSet(leaf_7.ecx, 11);
|
||||
features->avx512bitalg = IsBitSet(leaf_7.ecx, 12);
|
||||
features->avx512vpopcntdq = IsBitSet(leaf_7.ecx, 14);
|
||||
features->avx512_4vnniw = IsBitSet(leaf_7.edx, 2);
|
||||
features->avx512_4vbmi2 = IsBitSet(leaf_7.edx, 3);
|
||||
}
|
||||
}
|
||||
|
||||
static const X86Info kEmptyX86Info;
|
||||
|
||||
X86Info GetX86Info(void) {
|
||||
X86Info info = kEmptyX86Info;
|
||||
const Leaf leaf_0 = CpuId(0);
|
||||
const uint32_t max_cpuid_leaf = leaf_0.eax;
|
||||
SetVendor(leaf_0, info.vendor);
|
||||
if (IsVendor(leaf_0, "GenuineIntel") || IsVendor(leaf_0, "AuthenticAMD")) {
|
||||
ParseCpuId(max_cpuid_leaf, &info);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
#define CPUID(FAMILY, MODEL) (((FAMILY & 0xFF) << 8) | (MODEL & 0xFF))
|
||||
|
||||
X86Microarchitecture GetX86Microarchitecture(const X86Info* info) {
|
||||
if (memcmp(info->vendor, "GenuineIntel", sizeof(info->vendor)) == 0) {
|
||||
switch (CPUID(info->family, info->model)) {
|
||||
case CPUID(0x06, 0x35):
|
||||
case CPUID(0x06, 0x36):
|
||||
// https://en.wikipedia.org/wiki/Bonnell_(microarchitecture)
|
||||
return INTEL_ATOM_BNL;
|
||||
case CPUID(0x06, 0x37):
|
||||
case CPUID(0x06, 0x4C):
|
||||
// https://en.wikipedia.org/wiki/Silvermont
|
||||
return INTEL_ATOM_SMT;
|
||||
case CPUID(0x06, 0x5C):
|
||||
// https://en.wikipedia.org/wiki/Goldmont
|
||||
return INTEL_ATOM_GMT;
|
||||
case CPUID(0x06, 0x0F):
|
||||
case CPUID(0x06, 0x16):
|
||||
// https://en.wikipedia.org/wiki/Intel_Core_(microarchitecture)
|
||||
return INTEL_CORE;
|
||||
case CPUID(0x06, 0x17):
|
||||
case CPUID(0x06, 0x1D):
|
||||
// https://en.wikipedia.org/wiki/Penryn_(microarchitecture)
|
||||
return INTEL_PNR;
|
||||
case CPUID(0x06, 0x1A):
|
||||
case CPUID(0x06, 0x1E):
|
||||
case CPUID(0x06, 0x1F):
|
||||
case CPUID(0x06, 0x2E):
|
||||
// https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
|
||||
return INTEL_NHM;
|
||||
case CPUID(0x06, 0x25):
|
||||
case CPUID(0x06, 0x2C):
|
||||
case CPUID(0x06, 0x2F):
|
||||
// https://en.wikipedia.org/wiki/Westmere_(microarchitecture)
|
||||
return INTEL_WSM;
|
||||
case CPUID(0x06, 0x2A):
|
||||
case CPUID(0x06, 0x2D):
|
||||
// https://en.wikipedia.org/wiki/Sandy_Bridge#Models_and_steppings
|
||||
return INTEL_SNB;
|
||||
case CPUID(0x06, 0x3A):
|
||||
case CPUID(0x06, 0x3E):
|
||||
// https://en.wikipedia.org/wiki/Ivy_Bridge_(microarchitecture)#Models_and_steppings
|
||||
return INTEL_IVB;
|
||||
case CPUID(0x06, 0x3C):
|
||||
case CPUID(0x06, 0x3F):
|
||||
case CPUID(0x06, 0x45):
|
||||
case CPUID(0x06, 0x46):
|
||||
// https://en.wikipedia.org/wiki/Haswell_(microarchitecture)
|
||||
return INTEL_HSW;
|
||||
case CPUID(0x06, 0x3D):
|
||||
case CPUID(0x06, 0x47):
|
||||
case CPUID(0x06, 0x4F):
|
||||
case CPUID(0x06, 0x56):
|
||||
// https://en.wikipedia.org/wiki/Broadwell_(microarchitecture)
|
||||
return INTEL_BDW;
|
||||
case CPUID(0x06, 0x4E):
|
||||
case CPUID(0x06, 0x55):
|
||||
case CPUID(0x06, 0x5E):
|
||||
// https://en.wikipedia.org/wiki/Skylake_(microarchitecture)
|
||||
return INTEL_SKL;
|
||||
case CPUID(0x06, 0x8E):
|
||||
case CPUID(0x06, 0x9E):
|
||||
// https://en.wikipedia.org/wiki/Kaby_Lake
|
||||
return INTEL_KBL;
|
||||
default:
|
||||
return X86_UNKNOWN;
|
||||
}
|
||||
}
|
||||
if (memcmp(info->vendor, "AuthenticAMD", sizeof(info->vendor)) == 0) {
|
||||
switch (info->family) {
|
||||
// https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures
|
||||
case 0x0F:
|
||||
return AMD_HAMMER;
|
||||
case 0x10:
|
||||
return AMD_K10;
|
||||
case 0x14:
|
||||
return AMD_BOBCAT;
|
||||
case 0x15:
|
||||
return AMD_BULLDOZER;
|
||||
case 0x16:
|
||||
return AMD_JAGUAR;
|
||||
case 0x17:
|
||||
return AMD_ZEN;
|
||||
default:
|
||||
return X86_UNKNOWN;
|
||||
}
|
||||
}
|
||||
return X86_UNKNOWN;
|
||||
}
|
||||
|
||||
static void SetString(const uint32_t max_cpuid_ext_leaf, const uint32_t leaf_id,
|
||||
char* buffer) {
|
||||
const Leaf leaf = SafeCpuId(max_cpuid_ext_leaf, leaf_id);
|
||||
// We allow calling memcpy from SetString which is only called when requesting
|
||||
// X86BrandString.
|
||||
memcpy(buffer, &leaf, sizeof(Leaf));
|
||||
}
|
||||
|
||||
void FillX86BrandString(char brand_string[49]) {
|
||||
const Leaf leaf_ext_0 = CpuId(0x80000000);
|
||||
const uint32_t max_cpuid_leaf_ext = leaf_ext_0.eax;
|
||||
SetString(max_cpuid_leaf_ext, 0x80000002, brand_string);
|
||||
SetString(max_cpuid_leaf_ext, 0x80000003, brand_string + 16);
|
||||
SetString(max_cpuid_leaf_ext, 0x80000004, brand_string + 32);
|
||||
brand_string[48] = '\0';
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Introspection functions
|
||||
|
||||
int GetX86FeaturesEnumValue(const X86Features* features,
|
||||
X86FeaturesEnum value) {
|
||||
switch (value) {
|
||||
case X86_AES:
|
||||
return features->aes;
|
||||
case X86_ERMS:
|
||||
return features->erms;
|
||||
case X86_F16C:
|
||||
return features->f16c;
|
||||
case X86_FMA3:
|
||||
return features->fma3;
|
||||
case X86_VPCLMULQDQ:
|
||||
return features->vpclmulqdq;
|
||||
case X86_BMI1:
|
||||
return features->bmi1;
|
||||
case X86_BMI2:
|
||||
return features->bmi2;
|
||||
case X86_SSSE3:
|
||||
return features->ssse3;
|
||||
case X86_SSE4_1:
|
||||
return features->sse4_1;
|
||||
case X86_SSE4_2:
|
||||
return features->sse4_2;
|
||||
case X86_AVX:
|
||||
return features->avx;
|
||||
case X86_AVX2:
|
||||
return features->avx2;
|
||||
case X86_AVX512F:
|
||||
return features->avx512f;
|
||||
case X86_AVX512CD:
|
||||
return features->avx512cd;
|
||||
case X86_AVX512ER:
|
||||
return features->avx512er;
|
||||
case X86_AVX512PF:
|
||||
return features->avx512pf;
|
||||
case X86_AVX512BW:
|
||||
return features->avx512bw;
|
||||
case X86_AVX512DQ:
|
||||
return features->avx512dq;
|
||||
case X86_AVX512VL:
|
||||
return features->avx512vl;
|
||||
case X86_AVX512IFMA:
|
||||
return features->avx512ifma;
|
||||
case X86_AVX512VBMI:
|
||||
return features->avx512vbmi;
|
||||
case X86_AVX512VBMI2:
|
||||
return features->avx512vbmi2;
|
||||
case X86_AVX512VNNI:
|
||||
return features->avx512vnni;
|
||||
case X86_AVX512BITALG:
|
||||
return features->avx512bitalg;
|
||||
case X86_AVX512VPOPCNTDQ:
|
||||
return features->avx512vpopcntdq;
|
||||
case X86_AVX512_4VNNIW:
|
||||
return features->avx512_4vnniw;
|
||||
case X86_AVX512_4VBMI2:
|
||||
return features->avx512_4vbmi2;
|
||||
case X86_LAST_:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetX86FeaturesEnumName(X86FeaturesEnum value) {
|
||||
switch (value) {
|
||||
case X86_AES:
|
||||
return "aes";
|
||||
case X86_ERMS:
|
||||
return "erms";
|
||||
case X86_F16C:
|
||||
return "f16c";
|
||||
case X86_FMA3:
|
||||
return "fma3";
|
||||
case X86_VPCLMULQDQ:
|
||||
return "vpclmulqdq";
|
||||
case X86_BMI1:
|
||||
return "bmi1";
|
||||
case X86_BMI2:
|
||||
return "bmi2";
|
||||
case X86_SSSE3:
|
||||
return "ssse3";
|
||||
case X86_SSE4_1:
|
||||
return "sse4_1";
|
||||
case X86_SSE4_2:
|
||||
return "sse4_2";
|
||||
case X86_AVX:
|
||||
return "avx";
|
||||
case X86_AVX2:
|
||||
return "avx2";
|
||||
case X86_AVX512F:
|
||||
return "avx512f";
|
||||
case X86_AVX512CD:
|
||||
return "avx512cd";
|
||||
case X86_AVX512ER:
|
||||
return "avx512er";
|
||||
case X86_AVX512PF:
|
||||
return "avx512pf";
|
||||
case X86_AVX512BW:
|
||||
return "avx512bw";
|
||||
case X86_AVX512DQ:
|
||||
return "avx512dq";
|
||||
case X86_AVX512VL:
|
||||
return "avx512vl";
|
||||
case X86_AVX512IFMA:
|
||||
return "avx512ifma";
|
||||
case X86_AVX512VBMI:
|
||||
return "avx512vbmi";
|
||||
case X86_AVX512VBMI2:
|
||||
return "avx512vbmi2";
|
||||
case X86_AVX512VNNI:
|
||||
return "avx512vnni";
|
||||
case X86_AVX512BITALG:
|
||||
return "avx512bitalg";
|
||||
case X86_AVX512VPOPCNTDQ:
|
||||
return "avx512vpopcntdq";
|
||||
case X86_AVX512_4VNNIW:
|
||||
return "avx512_4vnniw";
|
||||
case X86_AVX512_4VBMI2:
|
||||
return "avx512_4vbmi2";
|
||||
case X86_LAST_:
|
||||
break;
|
||||
}
|
||||
return "unknown_feature";
|
||||
}
|
||||
|
||||
const char* GetX86MicroarchitectureName(X86Microarchitecture uarch) {
|
||||
switch (uarch) {
|
||||
case X86_UNKNOWN:
|
||||
return "X86_UNKNOWN";
|
||||
case INTEL_CORE:
|
||||
return "INTEL_CORE";
|
||||
case INTEL_PNR:
|
||||
return "INTEL_PNR";
|
||||
case INTEL_NHM:
|
||||
return "INTEL_NHM";
|
||||
case INTEL_ATOM_BNL:
|
||||
return "INTEL_ATOM_BNL";
|
||||
case INTEL_WSM:
|
||||
return "INTEL_WSM";
|
||||
case INTEL_SNB:
|
||||
return "INTEL_SNB";
|
||||
case INTEL_IVB:
|
||||
return "INTEL_IVB";
|
||||
case INTEL_ATOM_SMT:
|
||||
return "INTEL_ATOM_SMT";
|
||||
case INTEL_HSW:
|
||||
return "INTEL_HSW";
|
||||
case INTEL_BDW:
|
||||
return "INTEL_BDW";
|
||||
case INTEL_SKL:
|
||||
return "INTEL_SKL";
|
||||
case INTEL_ATOM_GMT:
|
||||
return "INTEL_ATOM_GMT";
|
||||
case INTEL_KBL:
|
||||
return "INTEL_KBL";
|
||||
case INTEL_CFL:
|
||||
return "INTEL_CFL";
|
||||
case INTEL_CNL:
|
||||
return "INTEL_CNL";
|
||||
case AMD_HAMMER:
|
||||
return "AMD_HAMMER";
|
||||
case AMD_K10:
|
||||
return "AMD_K10";
|
||||
case AMD_BOBCAT:
|
||||
return "AMD_BOBCAT";
|
||||
case AMD_BULLDOZER:
|
||||
return "AMD_BULLDOZER";
|
||||
case AMD_JAGUAR:
|
||||
return "AMD_JAGUAR";
|
||||
case AMD_ZEN:
|
||||
return "AMD_ZEN";
|
||||
}
|
||||
return "unknown microarchitecture";
|
||||
}
|
53
src/filesystem.c
Normal file
53
src/filesystem.c
Normal file
@ -0,0 +1,53 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/filesystem.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#include <io.h>
|
||||
int OpenFile(const char* filename) { return _open(filename, _O_RDONLY); }
|
||||
|
||||
void CloseFile(int file_descriptor) { _close(file_descriptor); }
|
||||
|
||||
int ReadFile(int file_descriptor, void* buffer, size_t buffer_size) {
|
||||
return _read(file_descriptor, buffer, buffer_size);
|
||||
}
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
|
||||
int OpenFile(const char* filename) {
|
||||
int result;
|
||||
do {
|
||||
result = open(filename, O_RDONLY);
|
||||
} while (result == -1L && errno == EINTR);
|
||||
return result;
|
||||
}
|
||||
|
||||
void CloseFile(int file_descriptor) { close(file_descriptor); }
|
||||
|
||||
int ReadFile(int file_descriptor, void* buffer, size_t buffer_size) {
|
||||
int result;
|
||||
do {
|
||||
result = read(file_descriptor, buffer, buffer_size);
|
||||
} while (result == -1L && errno == EINTR);
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
165
src/hwcaps.c
Normal file
165
src/hwcaps.c
Normal file
@ -0,0 +1,165 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/hwcaps.h"
|
||||
#include "cpu_features_macros.h"
|
||||
#include "internal/filesystem.h"
|
||||
|
||||
#if defined(NDEBUG)
|
||||
#define D(...)
|
||||
#else
|
||||
#include <stdio.h>
|
||||
#define D(...) \
|
||||
do { \
|
||||
printf(__VA_ARGS__); \
|
||||
fflush(stdout); \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
#if defined(CPU_FEATURES_ARCH_MIPS) || defined(CPU_FEATURES_ARCH_ANY_ARM)
|
||||
#define HWCAPS_ANDROID_MIPS_OR_ARM
|
||||
#endif
|
||||
|
||||
#if defined(CPU_FEATURES_OS_LINUX_OR_ANDROID) && \
|
||||
!defined(HWCAPS_ANDROID_MIPS_OR_ARM)
|
||||
#define HWCAPS_REGULAR_LINUX
|
||||
#endif
|
||||
|
||||
#if defined(HWCAPS_ANDROID_MIPS_OR_ARM) || defined(HWCAPS_REGULAR_LINUX)
|
||||
#define HWCAPS_SUPPORTED
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of GetElfHwcapFromGetauxval
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// On Linux we simply use getauxval.
|
||||
#if defined(HWCAPS_REGULAR_LINUX)
|
||||
#include <dlfcn.h>
|
||||
#include <sys/auxv.h>
|
||||
static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
|
||||
return getauxval(hwcap_type);
|
||||
}
|
||||
#endif // defined(HWCAPS_REGULAR_LINUX)
|
||||
|
||||
// On Android we probe the system's C library for a 'getauxval' function and
|
||||
// call it if it exits, or return 0 for failure. This function is available
|
||||
// since API level 20.
|
||||
//
|
||||
// This code does *NOT* check for '__ANDROID_API__ >= 20' to support the edge
|
||||
// case where some NDK developers use headers for a platform that is newer than
|
||||
// the one really targetted by their application. This is typically done to use
|
||||
// newer native APIs only when running on more recent Android versions, and
|
||||
// requires careful symbol management.
|
||||
//
|
||||
// Note that getauxval() can't really be re-implemented here, because its
|
||||
// implementation does not parse /proc/self/auxv. Instead it depends on values
|
||||
// that are passed by the kernel at process-init time to the C runtime
|
||||
// initialization layer.
|
||||
#if defined(HWCAPS_ANDROID_MIPS_OR_ARM)
|
||||
#include <dlfcn.h>
|
||||
#define AT_HWCAP 16
|
||||
#define AT_HWCAP2 26
|
||||
typedef unsigned long getauxval_func_t(unsigned long);
|
||||
|
||||
static uint32_t GetElfHwcapFromGetauxval(uint32_t hwcap_type) {
|
||||
uint32_t ret = 0;
|
||||
void* libc_handle = NULL;
|
||||
getauxval_func_t* func = NULL;
|
||||
|
||||
dlerror(); // Cleaning error state before calling dlopen.
|
||||
libc_handle = dlopen("libc.so", RTLD_NOW);
|
||||
if (!libc_handle) {
|
||||
D("Could not dlopen() C library: %s\n", dlerror());
|
||||
return 0;
|
||||
}
|
||||
func = (getauxval_func_t*)dlsym(libc_handle, "getauxval");
|
||||
if (!func) {
|
||||
D("Could not find getauxval() in C library\n");
|
||||
} else {
|
||||
// Note: getauxval() returns 0 on failure. Doesn't touch errno.
|
||||
ret = (uint32_t)(*func)(hwcap_type);
|
||||
}
|
||||
dlclose(libc_handle);
|
||||
return ret;
|
||||
}
|
||||
#endif // defined(HWCAPS_ANDROID_MIPS_OR_ARM)
|
||||
|
||||
#if defined(HWCAPS_SUPPORTED)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of GetHardwareCapabilities for Android and Linux
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Fallback when getauxval is not available, retrieves hwcaps from
|
||||
// "/proc/self/auxv".
|
||||
static uint32_t GetElfHwcapFromProcSelfAuxv(uint32_t hwcap_type) {
|
||||
struct {
|
||||
uint32_t tag;
|
||||
uint32_t value;
|
||||
} entry;
|
||||
uint32_t result = 0;
|
||||
const char filepath[] = "/proc/self/auxv";
|
||||
const int fd = OpenFile(filepath);
|
||||
if (fd < 0) {
|
||||
D("Could not open %s\n", filepath);
|
||||
return 0;
|
||||
}
|
||||
for (;;) {
|
||||
const int ret = ReadFile(fd, (char*)&entry, sizeof entry);
|
||||
if (ret < 0) {
|
||||
D("Error while reading %s\n", filepath);
|
||||
break;
|
||||
}
|
||||
// Detect end of list.
|
||||
if (ret == 0 || (entry.tag == 0 && entry.value == 0)) {
|
||||
break;
|
||||
}
|
||||
if (entry.tag == hwcap_type) {
|
||||
result = entry.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseFile(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Retrieves hardware capabilities by first trying to call getauxval, if not
|
||||
// available falls back to reading "/proc/self/auxv".
|
||||
static uint32_t GetHardwareCapabilitiesFor(uint32_t type) {
|
||||
uint32_t hwcaps = GetElfHwcapFromGetauxval(type);
|
||||
if (!hwcaps) {
|
||||
D("Parsing /proc/self/auxv to extract ELF hwcaps!\n");
|
||||
hwcaps = GetElfHwcapFromProcSelfAuxv(type);
|
||||
}
|
||||
return hwcaps;
|
||||
}
|
||||
|
||||
HardwareCapabilities GetHardwareCapabilities(void) {
|
||||
HardwareCapabilities capabilities;
|
||||
capabilities.hwcaps = GetHardwareCapabilitiesFor(AT_HWCAP);
|
||||
capabilities.hwcaps2 = GetHardwareCapabilitiesFor(AT_HWCAP2);
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
#else // (defined(HWCAPS_SUPPORTED)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Implementation of GetHardwareCapabilities for unsupported platforms.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const HardwareCapabilities kEmptyHardwareCapabilities;
|
||||
HardwareCapabilities GetHardwareCapabilities(void) {
|
||||
return kEmptyHardwareCapabilities;
|
||||
}
|
||||
#endif
|
48
src/linux_features_aggregator.c
Normal file
48
src/linux_features_aggregator.c
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/linux_features_aggregator.h"
|
||||
#include "internal/string_view.h"
|
||||
|
||||
void SetFromFlags(const size_t configs_size, const CapabilityConfig* configs,
|
||||
const StringView flags_line, void* const features) {
|
||||
size_t i = 0;
|
||||
for (; i < configs_size; ++i) {
|
||||
const CapabilityConfig config = configs[i];
|
||||
config.set_bit(features, HasWord(flags_line, config.proc_cpuinfo_flag));
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsSet(const uint32_t mask, const uint32_t value) {
|
||||
return (value & mask) == mask;
|
||||
}
|
||||
|
||||
static bool IsHwCapsSet(const HardwareCapabilities hwcaps_mask,
|
||||
const HardwareCapabilities hwcaps) {
|
||||
return IsSet(hwcaps_mask.hwcaps, hwcaps.hwcaps) &&
|
||||
IsSet(hwcaps_mask.hwcaps2, hwcaps.hwcaps2);
|
||||
}
|
||||
|
||||
void OverrideFromHwCaps(const size_t configs_size,
|
||||
const CapabilityConfig* configs,
|
||||
const HardwareCapabilities hwcaps,
|
||||
void* const features) {
|
||||
size_t i = 0;
|
||||
for (; i < configs_size; ++i) {
|
||||
const CapabilityConfig* config = &configs[i];
|
||||
if (IsHwCapsSet(config->hwcaps_mask, hwcaps)) {
|
||||
config->set_bit(features, true);
|
||||
}
|
||||
}
|
||||
}
|
111
src/list_cpu_features.cc
Normal file
111
src/list_cpu_features.cc
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cpu_features_macros.h"
|
||||
#include "cpuinfo_aarch64.h"
|
||||
#include "cpuinfo_arm.h"
|
||||
#include "cpuinfo_mips.h"
|
||||
#include "cpuinfo_x86.h"
|
||||
|
||||
namespace cpu_features {
|
||||
|
||||
// Prints a named numeric value in both decimal and hexadecimal.
|
||||
void PrintN(const char* field, int value) {
|
||||
printf("%-15s : %3d (0x%02X)\n", field, value, value);
|
||||
}
|
||||
|
||||
// Prints a named string.
|
||||
void PrintS(const char* field, const char* value) {
|
||||
printf("%-15s : %s\n", field, value);
|
||||
}
|
||||
|
||||
template <typename HasFeatureFun, typename FeatureNameFun, typename FeatureType,
|
||||
typename EnumType>
|
||||
std::string GetFlags(const HasFeatureFun HasFeature,
|
||||
const FeatureNameFun FeatureName,
|
||||
const FeatureType* features, const EnumType last) {
|
||||
std::vector<std::string> flags;
|
||||
for (int i = 0; i < last; ++i) {
|
||||
const EnumType enum_value = static_cast<EnumType>(i);
|
||||
if (HasFeature(features, enum_value)) {
|
||||
flags.push_back(FeatureName(enum_value));
|
||||
}
|
||||
}
|
||||
std::sort(flags.begin(), flags.end());
|
||||
std::string buffer;
|
||||
for (const auto& flag : flags) {
|
||||
if (!buffer.empty()) buffer += ' ';
|
||||
buffer += flag;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void Main() {
|
||||
#if defined(CPU_FEATURES_ARCH_X86)
|
||||
char brand_string[49];
|
||||
const X86Info info = GetX86Info();
|
||||
const auto flags = GetFlags(&GetX86FeaturesEnumValue, &GetX86FeaturesEnumName,
|
||||
&info.features, X86FeaturesEnum::X86_LAST_);
|
||||
FillX86BrandString(brand_string);
|
||||
PrintS("arch", "x86");
|
||||
PrintS("brand", brand_string);
|
||||
PrintN("family", info.family);
|
||||
PrintN("model", info.model);
|
||||
PrintN("stepping", info.stepping);
|
||||
PrintS("uarch", GetX86MicroarchitectureName(GetX86Microarchitecture(&info)));
|
||||
PrintS("flags", flags.c_str());
|
||||
#elif defined(CPU_FEATURES_ARCH_ARM)
|
||||
const ArmInfo info = GetArmInfo();
|
||||
const auto flags = GetFlags(&GetArmFeaturesEnumValue, &GetArmFeaturesEnumName,
|
||||
&info.features, ArmFeaturesEnum::ARM_LAST_);
|
||||
PrintS("arch", "ARM");
|
||||
PrintN("implementer", info.implementer);
|
||||
PrintN("architecture", info.architecture);
|
||||
PrintN("variant", info.variant);
|
||||
PrintN("part", info.part);
|
||||
PrintN("revision", info.revision);
|
||||
PrintS("flags", flags.c_str());
|
||||
#elif defined(CPU_FEATURES_ARCH_AARCH64)
|
||||
const Aarch64Info info = GetAarch64Info();
|
||||
const auto flags =
|
||||
GetFlags(&GetAarch64FeaturesEnumValue, &GetAarch64FeaturesEnumName,
|
||||
&info.features, Aarch64FeaturesEnum::AARCH64_LAST_);
|
||||
PrintS("arch", "aarch64");
|
||||
PrintN("implementer", info.implementer);
|
||||
PrintN("variant", info.variant);
|
||||
PrintN("part", info.part);
|
||||
PrintN("revision", info.revision);
|
||||
PrintS("flags", flags.c_str());
|
||||
#elif defined(CPU_FEATURES_ARCH_MIPS)
|
||||
const MipsInfo info = GetMipsInfo();
|
||||
const auto flags =
|
||||
GetFlags(&GetMipsFeaturesEnumValue, &GetMipsFeaturesEnumName,
|
||||
&info.features, MipsFeaturesEnum::MIPS_LAST_);
|
||||
PrintS("arch", "mips");
|
||||
PrintS("flags", flags.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace cpu_features
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
cpu_features::Main();
|
||||
return 0;
|
||||
}
|
128
src/stack_line_reader.c
Normal file
128
src/stack_line_reader.c
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/stack_line_reader.h"
|
||||
#include "internal/filesystem.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
void StackLineReader_Initialize(StackLineReader* reader, int fd) {
|
||||
reader->view.ptr = reader->buffer;
|
||||
reader->view.size = 0;
|
||||
reader->skip_mode = false;
|
||||
reader->fd = fd;
|
||||
}
|
||||
|
||||
// Replaces the content of buffer with bytes from the file.
|
||||
static int LoadFullBuffer(StackLineReader* reader) {
|
||||
const int read =
|
||||
ReadFile(reader->fd, reader->buffer, STACK_LINE_READER_BUFFER_SIZE);
|
||||
assert(read >= 0);
|
||||
reader->view.ptr = reader->buffer;
|
||||
reader->view.size = read;
|
||||
return read;
|
||||
}
|
||||
|
||||
// Appends with bytes from the file to buffer, filling the remaining space.
|
||||
static int LoadMore(StackLineReader* reader) {
|
||||
char* const ptr = reader->buffer + reader->view.size;
|
||||
const size_t size_to_read = STACK_LINE_READER_BUFFER_SIZE - reader->view.size;
|
||||
const int read = ReadFile(reader->fd, ptr, size_to_read);
|
||||
assert(read >= 0);
|
||||
assert(read <= (int)size_to_read);
|
||||
reader->view.size += read;
|
||||
return read;
|
||||
}
|
||||
|
||||
static int IndexOfEol(StackLineReader* reader) {
|
||||
return IndexOfChar(reader->view, '\n');
|
||||
}
|
||||
|
||||
// Relocate buffer's pending bytes at the beginning of the array and fills the
|
||||
// remaining space with bytes from the file.
|
||||
static int BringToFrontAndLoadMore(StackLineReader* reader) {
|
||||
if (reader->view.size && reader->view.ptr != reader->buffer) {
|
||||
memmove(reader->buffer, reader->view.ptr, reader->view.size);
|
||||
}
|
||||
reader->view.ptr = reader->buffer;
|
||||
return LoadMore(reader);
|
||||
}
|
||||
|
||||
// Loads chunks of buffer size from disks until it contains a newline character
|
||||
// or end of file.
|
||||
static void SkipToNextLine(StackLineReader* reader) {
|
||||
for (;;) {
|
||||
const int read = LoadFullBuffer(reader);
|
||||
if (read == 0) {
|
||||
break;
|
||||
} else {
|
||||
const int eol_index = IndexOfEol(reader);
|
||||
if (eol_index >= 0) {
|
||||
reader->view = PopFront(reader->view, eol_index + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LineResult CreateLineResult(bool eof, bool full_line, StringView view) {
|
||||
LineResult result;
|
||||
result.eof = eof;
|
||||
result.full_line = full_line;
|
||||
result.line = view;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper methods to provide clearer semantic in StackLineReader_NextLine.
|
||||
static LineResult CreateEOFLineResult(StringView view) {
|
||||
return CreateLineResult(true, true, view);
|
||||
}
|
||||
|
||||
static LineResult CreateTruncatedLineResult(StringView view) {
|
||||
return CreateLineResult(false, false, view);
|
||||
}
|
||||
|
||||
static LineResult CreateValidLineResult(StringView view) {
|
||||
return CreateLineResult(false, true, view);
|
||||
}
|
||||
|
||||
LineResult StackLineReader_NextLine(StackLineReader* reader) {
|
||||
if (reader->skip_mode) {
|
||||
SkipToNextLine(reader);
|
||||
reader->skip_mode = false;
|
||||
}
|
||||
{
|
||||
const bool can_load_more =
|
||||
reader->view.size < STACK_LINE_READER_BUFFER_SIZE;
|
||||
int eol_index = IndexOfEol(reader);
|
||||
if (eol_index < 0 && can_load_more) {
|
||||
const int read = BringToFrontAndLoadMore(reader);
|
||||
if (read == 0) {
|
||||
return CreateEOFLineResult(reader->view);
|
||||
}
|
||||
eol_index = IndexOfEol(reader);
|
||||
}
|
||||
if (eol_index < 0) {
|
||||
reader->skip_mode = true;
|
||||
return CreateTruncatedLineResult(reader->view);
|
||||
}
|
||||
{
|
||||
StringView line = KeepFront(reader->view, eol_index);
|
||||
reader->view = PopFront(reader->view, eol_index + 1);
|
||||
return CreateValidLineResult(line);
|
||||
}
|
||||
}
|
||||
}
|
163
src/string_view.c
Normal file
163
src/string_view.c
Normal file
@ -0,0 +1,163 @@
|
||||
// Copyright 2017 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "internal/string_view.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
int IndexOfChar(const StringView view, char c) {
|
||||
if (view.ptr && view.size) {
|
||||
const char* const found = (const char*)memchr(view.ptr, c, view.size);
|
||||
if (found) {
|
||||
return found - view.ptr;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int IndexOf(const StringView view, const StringView sub_view) {
|
||||
if (sub_view.size) {
|
||||
StringView remainder = view;
|
||||
while (remainder.size >= sub_view.size) {
|
||||
const int found_index = IndexOfChar(remainder, sub_view.ptr[0]);
|
||||
if (found_index < 0) break;
|
||||
remainder = PopFront(remainder, found_index);
|
||||
if (StartsWith(remainder, sub_view)) {
|
||||
return remainder.ptr - view.ptr;
|
||||
}
|
||||
remainder = PopFront(remainder, 1);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool IsEquals(const StringView a, const StringView b) {
|
||||
if (a.size == b.size) {
|
||||
return a.ptr == b.ptr || memcmp(a.ptr, b.ptr, b.size) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool StartsWith(const StringView a, const StringView b) {
|
||||
return a.ptr && b.ptr && b.size && a.size >= b.size
|
||||
? memcmp(a.ptr, b.ptr, b.size) == 0
|
||||
: false;
|
||||
}
|
||||
|
||||
StringView PopFront(const StringView str_view, size_t count) {
|
||||
if (count > str_view.size) {
|
||||
return kEmptyStringView;
|
||||
}
|
||||
return view(str_view.ptr + count, str_view.size - count);
|
||||
}
|
||||
|
||||
StringView PopBack(const StringView str_view, size_t count) {
|
||||
if (count > str_view.size) {
|
||||
return kEmptyStringView;
|
||||
}
|
||||
return view(str_view.ptr, str_view.size - count);
|
||||
}
|
||||
|
||||
StringView KeepFront(const StringView str_view, size_t count) {
|
||||
return count <= str_view.size ? view(str_view.ptr, count) : str_view;
|
||||
}
|
||||
|
||||
char Front(const StringView view) {
|
||||
assert(view.size);
|
||||
assert(view.ptr);
|
||||
return view.ptr[0];
|
||||
}
|
||||
|
||||
char Back(const StringView view) {
|
||||
assert(view.size);
|
||||
return view.ptr[view.size - 1];
|
||||
}
|
||||
|
||||
StringView TrimWhitespace(StringView view) {
|
||||
while (view.size && isspace(Front(view))) view = PopFront(view, 1);
|
||||
while (view.size && isspace(Back(view))) view = PopBack(view, 1);
|
||||
return view;
|
||||
}
|
||||
|
||||
static int HexValue(const char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Returns -1 if view contains non digits.
|
||||
static int ParsePositiveNumberWithBase(const StringView view, int base) {
|
||||
int result = 0;
|
||||
StringView remainder = view;
|
||||
for (; remainder.size; remainder = PopFront(remainder, 1)) {
|
||||
const int value = HexValue(Front(remainder));
|
||||
if (value < 0 || value >= base) return -1;
|
||||
result = (result * base) + value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int ParsePositiveNumber(const StringView view) {
|
||||
if (view.size) {
|
||||
const StringView hex_prefix = str("0x");
|
||||
if (StartsWith(view, hex_prefix)) {
|
||||
const StringView span_no_prefix = PopFront(view, hex_prefix.size);
|
||||
return ParsePositiveNumberWithBase(span_no_prefix, 16);
|
||||
}
|
||||
return ParsePositiveNumberWithBase(view, 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void CopyString(const StringView src, char* dst, size_t dst_size) {
|
||||
if (dst_size > 0) {
|
||||
const size_t max_copy_size = dst_size - 1;
|
||||
const size_t copy_size =
|
||||
src.size > max_copy_size ? max_copy_size : src.size;
|
||||
memcpy(dst, src.ptr, copy_size);
|
||||
dst[copy_size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool HasWord(const StringView line, const char* const word_str) {
|
||||
const StringView word = str(word_str);
|
||||
StringView remainder = line;
|
||||
for (;;) {
|
||||
const int index_of_word = IndexOf(remainder, word);
|
||||
if (index_of_word < 0) {
|
||||
return false;
|
||||
} else {
|
||||
const StringView before = KeepFront(line, index_of_word);
|
||||
const StringView after = PopFront(line, index_of_word + word.size);
|
||||
const bool valid_before = before.size == 0 || Back(before) == ' ';
|
||||
const bool valid_after = after.size == 0 || Front(after) == ' ';
|
||||
if (valid_before && valid_after) return true;
|
||||
remainder = PopFront(remainder, index_of_word + word.size);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetAttributeKeyValue(const StringView line, StringView* key,
|
||||
StringView* value) {
|
||||
const StringView sep = str(": ");
|
||||
const int index_of_separator = IndexOf(line, sep);
|
||||
if (index_of_separator < 0) return false;
|
||||
*value = TrimWhitespace(PopFront(line, index_of_separator + sep.size));
|
||||
*key = TrimWhitespace(KeepFront(line, index_of_separator));
|
||||
return true;
|
||||
}
|
Reference in New Issue
Block a user