Adding iree_bitfield_parse utility.
diff --git a/runtime/src/iree/base/bitfield.c b/runtime/src/iree/base/bitfield.c
index 428220c..c395940 100644
--- a/runtime/src/iree/base/bitfield.c
+++ b/runtime/src/iree/base/bitfield.c
@@ -9,6 +9,61 @@
#include <stdlib.h>
#include <string.h>
+static bool iree_bitfield_lookup_mapping(
+ iree_string_view_t value, iree_host_size_t mapping_count,
+ const iree_bitfield_string_mapping_t* mappings, uint32_t* out_bits) {
+ *out_bits = 0;
+ for (iree_host_size_t mapping_index = 0; mapping_index < mapping_count;
+ ++mapping_index) {
+ const iree_bitfield_string_mapping_t mapping = mappings[mapping_index];
+ if (iree_string_view_equal_case(mapping.string, value)) {
+ *out_bits = mapping.bits;
+ return true;
+ }
+ }
+ return false;
+}
+
+static inline bool iree_isdigit(char c) { return (unsigned)c - '0' < 10; }
+
+IREE_API_EXPORT iree_status_t iree_bitfield_parse(
+ iree_string_view_t value, iree_host_size_t mapping_count,
+ const iree_bitfield_string_mapping_t* mappings, uint32_t* out_value) {
+ uint32_t bits_value = 0;
+ while (!iree_string_view_is_empty(value)) {
+ // Slice off the next part (or the tail).
+ iree_string_view_t part = iree_string_view_empty();
+ iree_string_view_split(value, '|', &part, &value);
+ part = iree_string_view_trim(part);
+ if (iree_string_view_is_empty(part)) continue;
+
+ // Scan the mapping table and match case-insensitive.
+ uint32_t mapping_bits = 0;
+ if (iree_bitfield_lookup_mapping(part, mapping_count, mappings,
+ &mapping_bits)) {
+ bits_value |= mapping_bits;
+ continue;
+ }
+
+ // If it starts with a number we try to parse it like one.
+ if (iree_isdigit(part.data[0])) {
+ uint32_t int_bits = 0;
+ if (iree_string_view_atoi_uint32(part, &int_bits)) {
+ bits_value |= int_bits;
+ continue;
+ }
+ }
+
+ // Unknown bitfield value.
+ return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
+ "unrecognized bitfield member '%.*s'",
+ (int)part.size, part.data);
+ }
+
+ *out_value = bits_value;
+ return iree_ok_status();
+}
+
IREE_API_EXPORT iree_status_t
iree_bitfield_format(uint32_t value, iree_host_size_t mapping_count,
const iree_bitfield_string_mapping_t* mappings,
diff --git a/runtime/src/iree/base/bitfield.h b/runtime/src/iree/base/bitfield.h
index 4329d53..6f7660f 100644
--- a/runtime/src/iree/base/bitfield.h
+++ b/runtime/src/iree/base/bitfield.h
@@ -30,7 +30,30 @@
iree_string_view_t string;
} iree_bitfield_string_mapping_t;
-// Appends the formatted contents of the given bitfield value.
+// Parses the bitfield |value| from a string.
+// The provided |mappings| table is used for string lookup. Unknown values
+// result in a failure.
+//
+// Usage:
+// // Static mapping table:
+// static const iree_bitfield_string_mapping_t my_bitfield_mappings[] = {
+// {MY_BITFIELD_ALL, IREE_SVL("ALL")}, // combined flags first
+// {MY_BITFIELD_A, IREE_SVL("A")},
+// {MY_BITFIELD_B, IREE_SVL("B")},
+// {MY_BITFIELD_C, IREE_SVL("C")},
+// };
+//
+// // Produces the bits MY_BITFIELD_A|MY_BITFIELD_B:
+// uint32_t value_ab = 0;
+// IREE_RETURN_IF_ERROR(iree_bitfield_parse(
+// IREE_SV("A|B"),
+// IREE_ARRAYSIZE(my_bitfield_mappings), my_bitfield_mappings,
+// &value_ab));
+IREE_API_EXPORT iree_status_t iree_bitfield_parse(
+ iree_string_view_t value, iree_host_size_t mapping_count,
+ const iree_bitfield_string_mapping_t* mappings, uint32_t* out_value);
+
+// Appends the formatted contents of the given bitfield |value|.
// Processes values in the order of the mapping table provided and will only
// use each bit once. Use this to prioritize combined flags over split ones.
//
diff --git a/runtime/src/iree/base/bitfield_test.cc b/runtime/src/iree/base/bitfield_test.cc
index 95109ed..2989ce2 100644
--- a/runtime/src/iree/base/bitfield_test.cc
+++ b/runtime/src/iree/base/bitfield_test.cc
@@ -13,6 +13,9 @@
namespace iree {
namespace {
+using iree::testing::status::IsOkAndHolds;
+using iree::testing::status::StatusIs;
+
enum my_bitfield_e {
MY_BITFIELD_NONE = 0,
MY_BITFIELD_A = 1 << 0,
@@ -22,6 +25,62 @@
typedef uint32_t my_bitfield_t;
template <size_t mapping_count>
+StatusOr<uint32_t> ParseBitfieldValue(
+ const char* value,
+ const iree_bitfield_string_mapping_t (&mappings)[mapping_count]) {
+ uint32_t bits_value = 0;
+ IREE_RETURN_IF_ERROR(iree_bitfield_parse(
+ iree_make_cstring_view(value), mapping_count, mappings, &bits_value));
+ return bits_value;
+}
+
+// Tests general parser usage.
+TEST(BitfieldTest, ParseBitfieldValue) {
+ static const iree_bitfield_string_mapping_t mappings[] = {
+ {MY_BITFIELD_A, IREE_SV("A")},
+ {MY_BITFIELD_B, IREE_SV("B")},
+ };
+ EXPECT_THAT(ParseBitfieldValue("", mappings), IsOkAndHolds(MY_BITFIELD_NONE));
+ EXPECT_THAT(ParseBitfieldValue("A", mappings), IsOkAndHolds(MY_BITFIELD_A));
+ EXPECT_THAT(ParseBitfieldValue("A|B", mappings),
+ IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+ EXPECT_THAT(ParseBitfieldValue("a|b", mappings),
+ IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+ EXPECT_THAT(ParseBitfieldValue("|a||B|", mappings),
+ IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+}
+
+// Tests that empty mapping tables behave ok.
+TEST(BitfieldTest, ParseBitfieldValueEmpty) {
+ static const iree_bitfield_string_mapping_t mappings[1] = {
+ {0, IREE_SV("UNUSED")}, // unused; required for C++ compat
+ };
+ // Empty strings always mean 0, no mapping fields needed.
+ EXPECT_THAT(ParseBitfieldValue("", mappings), IsOkAndHolds(0));
+ // If any named values are provided, though, we fail.
+ EXPECT_THAT(ParseBitfieldValue("foo", mappings),
+ StatusIs(StatusCode::kInvalidArgument));
+ // Manually-specified values are ok, though.
+ EXPECT_THAT(ParseBitfieldValue("2h|1h", mappings), IsOkAndHolds(0x2u | 0x1u));
+}
+
+// Tests that values not found in the mappings are still parsed.
+TEST(BitfieldTest, ParseBitfieldValueUnhandledValues) {
+ static const iree_bitfield_string_mapping_t mappings[] = {
+ {MY_BITFIELD_A, IREE_SV("A")},
+ {MY_BITFIELD_B, IREE_SV("B")},
+ };
+ EXPECT_THAT(ParseBitfieldValue("A|2", mappings),
+ IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+ EXPECT_THAT(ParseBitfieldValue("A|2h", mappings),
+ IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+ EXPECT_THAT(ParseBitfieldValue("A|0x2", mappings),
+ IsOkAndHolds(MY_BITFIELD_A | MY_BITFIELD_B));
+ EXPECT_THAT(ParseBitfieldValue("A|a08", mappings),
+ StatusIs(StatusCode::kInvalidArgument));
+}
+
+template <size_t mapping_count>
std::string FormatBitfieldValue(
uint32_t value,
const iree_bitfield_string_mapping_t (&mappings)[mapping_count]) {
@@ -30,7 +89,7 @@
return std::string(sv.data, sv.size);
}
-// Tests general usage.
+// Tests general formatting usage.
TEST(BitfieldTest, FormatBitfieldValue) {
static const iree_bitfield_string_mapping_t mappings[] = {
{MY_BITFIELD_A, IREE_SV("A")},
@@ -45,7 +104,7 @@
// Tests that empty mapping tables are fine.
TEST(BitfieldTest, FormatBitfieldValueEmpty) {
static const iree_bitfield_string_mapping_t mappings[1] = {
- {0, IREE_SV("UNUSED")},
+ {0, IREE_SV("UNUSED")}, // unused; required for C++ compat
};
iree_bitfield_string_temp_t temp;
auto sv = iree_bitfield_format_inline(MY_BITFIELD_NONE, 0, mappings, &temp);
diff --git a/runtime/src/iree/base/string_view.c b/runtime/src/iree/base/string_view.c
index 8b6153e..477a473 100644
--- a/runtime/src/iree/base/string_view.c
+++ b/runtime/src/iree/base/string_view.c
@@ -18,6 +18,16 @@
return a < b ? a : b;
}
+// Here to ensure that we don't pull in locale-specific code:
+static bool iree_isupper(char c) { return (unsigned)c - 'A' < 26; }
+static bool iree_islower(char c) { return (unsigned)c - 'a' < 26; }
+static inline char iree_toupper(char c) {
+ return iree_islower(c) ? (c & 0x5F) : c;
+}
+static inline char iree_tolower(char c) {
+ return iree_isupper(c) ? (c | 32) : c;
+}
+
IREE_API_EXPORT bool iree_string_view_equal(iree_string_view_t lhs,
iree_string_view_t rhs) {
if (lhs.size != rhs.size) return false;
@@ -27,6 +37,15 @@
return true;
}
+IREE_API_EXPORT bool iree_string_view_equal_case(iree_string_view_t lhs,
+ iree_string_view_t rhs) {
+ if (lhs.size != rhs.size) return false;
+ for (iree_host_size_t i = 0; i < lhs.size; ++i) {
+ if (iree_tolower(lhs.data[i]) != iree_tolower(rhs.data[i])) return false;
+ }
+ return true;
+}
+
IREE_API_EXPORT int iree_string_view_compare(iree_string_view_t lhs,
iree_string_view_t rhs) {
iree_host_size_t min_size = iree_min_host_size(lhs.size, rhs.size);
diff --git a/runtime/src/iree/base/string_view.h b/runtime/src/iree/base/string_view.h
index 1026772..65adcde 100644
--- a/runtime/src/iree/base/string_view.h
+++ b/runtime/src/iree/base/string_view.h
@@ -99,6 +99,10 @@
// Returns true if the two strings are equal (compare == 0).
IREE_API_EXPORT bool iree_string_view_equal(iree_string_view_t lhs,
iree_string_view_t rhs);
+// Returns true if the two strings are equal (compare == 0) ignoring case.
+// Equivalent to strcasecmp.
+IREE_API_EXPORT bool iree_string_view_equal_case(iree_string_view_t lhs,
+ iree_string_view_t rhs);
// Like std::string::compare but with iree_string_view_t values.
IREE_API_EXPORT int iree_string_view_compare(iree_string_view_t lhs,
diff --git a/runtime/src/iree/base/string_view_test.cc b/runtime/src/iree/base/string_view_test.cc
index 27fe076..3589cb3 100644
--- a/runtime/src/iree/base/string_view_test.cc
+++ b/runtime/src/iree/base/string_view_test.cc
@@ -35,6 +35,25 @@
EXPECT_FALSE(equal("b", "ab"));
EXPECT_TRUE(equal("abc", "abc"));
EXPECT_FALSE(equal("abc", "aBc"));
+ EXPECT_TRUE(equal("a_c", "a_c"));
+}
+
+TEST(StringViewTest, EqualCase) {
+ auto equal_case = [](const char* lhs, const char* rhs) -> bool {
+ return iree_string_view_equal_case(iree_make_cstring_view(lhs),
+ iree_make_cstring_view(rhs));
+ };
+ EXPECT_TRUE(equal_case("", ""));
+ EXPECT_FALSE(equal_case("a", ""));
+ EXPECT_FALSE(equal_case("", "a"));
+ EXPECT_TRUE(equal_case("a", "a"));
+ EXPECT_TRUE(equal_case("A", "a"));
+ EXPECT_TRUE(equal_case("a", "A"));
+ EXPECT_FALSE(equal_case("a", "ab"));
+ EXPECT_FALSE(equal_case("b", "ab"));
+ EXPECT_TRUE(equal_case("abc", "abc"));
+ EXPECT_TRUE(equal_case("abc", "aBc"));
+ EXPECT_TRUE(equal_case("a_c", "a_C"));
}
TEST(StringViewTest, FindChar) {