The string_slice inherits from array_slice and is used to refer to a substring of an array that is memory managed elsewhere without modifying the underlying array.
For example, this is useful in cases such as when needing to refer to a substring of an attribute in the syntax tree. This commit also adds some minimal helper functions for string_slice, such as strtok, strcmp, and a function to strip whitespace from the beginning and end of a slice. gcc/ChangeLog: * vec.cc (string_slice::strtok): New method. (strcmp): Add implementation for string_slice. (string_slice::strip): New method. (test_string_slice_initializers): New test. (test_string_slice_strtok): Ditto. (test_string_slice_strcmp): Ditto. (test_string_slice_equality): Ditto. (test_string_slice_invalid): Ditto. (test_string_slice_strip): Ditto. (vec_cc_tests): Add new tests. * vec.h (class string_slice): New class. (strcmp): Add implementation for string_slice. --- gcc/vec.cc | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/vec.h | 38 +++++++++++++ 2 files changed, 195 insertions(+)
diff --git a/gcc/vec.cc b/gcc/vec.cc index 55f5f3dd447..569dbf2a53c 100644 --- a/gcc/vec.cc +++ b/gcc/vec.cc @@ -176,6 +176,67 @@ dump_vec_loc_statistics (void) vec_mem_desc.dump (VEC_ORIGIN); } +string_slice +string_slice::strtok (string_slice *str, string_slice delims) +{ + const char *ptr = str->begin (); + + /* If the input string is empty or invalid, return an invalid slice + as there are no more tokens to return. */ + if (str->empty () || !str->is_valid ()) + { + *str = string_slice::invalid (); + return string_slice::invalid (); + } + + for (; ptr < str->end (); ptr++) + for (const char *c = delims.begin (); c < delims.end(); c++) + if (*ptr == *c) + { + const char *start = str->begin (); + /* Update the input string to be the remaining string. */ + *str = string_slice ((ptr + 1), str->end () - ptr - 1); + return string_slice (start, (size_t) (ptr - start)); + } + + /* If no deliminators between the start and end, return the whole string. */ + string_slice res = *str; + *str = string_slice::invalid (); + return res; +} + +int +strcmp (string_slice str1, string_slice str2) +{ + for (unsigned int i = 0; i < str1.size () && i < str2.size (); i++) + { + if (str1[i] < str2[i]) + return -1; + if (str1[i] > str2[i]) + return 1; + } + + if (str1.size () < str2.size ()) + return -1; + if (str1.size () > str2.size ()) + return 1; + return 0; +} + +string_slice +string_slice::strip () +{ + const char *start = this->begin (); + const char *end = this->end (); + + while (start < end && ISSPACE (*start)) + start++; + while (end > start && ISSPACE (*(end-1))) + end--; + + return string_slice (start, end-start); +} + #if CHECKING_P /* Report qsort comparator CMP consistency check failure with P1, P2, P3 as witness elements. */ @@ -584,6 +645,96 @@ test_auto_alias () ASSERT_EQ (val, 0); } +static void +test_string_slice_initializers () +{ + string_slice str1 = string_slice (); + ASSERT_TRUE (str1.is_valid ()); + ASSERT_EQ (str1.size (), 0); + + string_slice str2 = string_slice ("Test string"); + ASSERT_TRUE (str2.is_valid ()); + ASSERT_EQ (str2.size (), 11); + + string_slice str3 = string_slice ("Test string", 4); + ASSERT_TRUE (str3.is_valid ()); + ASSERT_EQ (str3.size (), 4); +} + +static void +test_string_slice_strtok () +{ + const char *test_string + = "This is the test string, it \0 is for testing, 123 ,,"; + + string_slice test_string_slice = string_slice (test_string, 53); + string_slice test_delims = string_slice (",\0", 2); + + ASSERT_EQ (string_slice::strtok (&test_string_slice, test_delims), + string_slice ("This is the test string")); + ASSERT_EQ (string_slice::strtok (&test_string_slice, test_delims), + string_slice (" it ")); + ASSERT_EQ (string_slice::strtok (&test_string_slice, test_delims), + string_slice (" is for testing")); + ASSERT_EQ (string_slice::strtok (&test_string_slice, test_delims), + string_slice (" 123 ")); + ASSERT_EQ (string_slice::strtok (&test_string_slice, test_delims), + string_slice ("")); + ASSERT_EQ (string_slice::strtok (&test_string_slice, test_delims), + string_slice ("")); + ASSERT_TRUE (test_string_slice.empty ()); + ASSERT_FALSE (string_slice::strtok (&test_string_slice, test_delims) + .is_valid ()); + ASSERT_FALSE (test_string_slice.is_valid ()); +} + +static void +test_string_slice_strcmp () +{ + ASSERT_EQ (strcmp (string_slice (), string_slice ()), 0); + ASSERT_EQ (strcmp (string_slice ("test"), string_slice ()), 1); + ASSERT_EQ (strcmp (string_slice (), string_slice ("test")), -1); + ASSERT_EQ (strcmp (string_slice ("test"), string_slice ("test")), 0); + ASSERT_EQ (strcmp (string_slice ("a"), string_slice ("b")), -1); + ASSERT_EQ (strcmp (string_slice ("b"), string_slice ("a")), 1); + ASSERT_EQ (strcmp (string_slice ("ab", 1), string_slice ("a")), 0); + ASSERT_EQ (strcmp (string_slice ("ab", 2), string_slice ("a")), 1); +} + +static void +test_string_slice_equality () +{ + ASSERT_TRUE (string_slice () == string_slice ()); + ASSERT_FALSE (string_slice ("test") == string_slice ()); + ASSERT_FALSE (string_slice () == string_slice ("test")); + ASSERT_TRUE (string_slice ("test") == string_slice ("test")); + ASSERT_FALSE (string_slice ("a") == string_slice ("b")); + ASSERT_FALSE (string_slice ("b") == string_slice ("a")); + ASSERT_TRUE (string_slice ("ab", 1) == string_slice ("a")); + ASSERT_FALSE (string_slice ("ab", 2) == string_slice ("a")); +} + +static void +test_string_slice_invalid () +{ + ASSERT_FALSE (string_slice::invalid ().is_valid ()); + ASSERT_FALSE (string_slice (NULL, 1).is_valid ()); + ASSERT_TRUE (string_slice (NULL, 0).is_valid ()); + ASSERT_TRUE (string_slice ("Test", 0).is_valid ()); + ASSERT_TRUE (string_slice ().is_valid ()); +} + +static void +test_string_slice_strip () +{ + ASSERT_EQ (string_slice (" test ").strip (), string_slice ("test")); + ASSERT_EQ (string_slice ("\t test string\t \n ").strip (), + string_slice ("test string")); + ASSERT_EQ (string_slice ("test").strip (), string_slice ("test")); + ASSERT_EQ (string_slice ().strip (), string_slice ()); + ASSERT_EQ (string_slice ("\t \n \t ").strip (), string_slice ()); +} + /* Run all of the selftests within this file. */ void @@ -604,6 +755,12 @@ vec_cc_tests () test_reverse (); test_auto_delete_vec (); test_auto_alias (); + test_string_slice_initializers (); + test_string_slice_strtok (); + test_string_slice_strcmp (); + test_string_slice_equality (); + test_string_slice_invalid (); + test_string_slice_strip (); } } // namespace selftest diff --git a/gcc/vec.h b/gcc/vec.h index 915df06f03e..409cdab5bc3 100644 --- a/gcc/vec.h +++ b/gcc/vec.h @@ -2484,4 +2484,42 @@ make_array_slice (T *base, unsigned int size) # pragma GCC poison m_vec m_vecpfx m_vecdata #endif +/* string_slice inherits from array_slice, specifically to refer to a substring + of a character array. + It includes some string like helpers. */ +class string_slice; + +int +strcmp (string_slice str1, string_slice str2); + +class string_slice : public array_slice<const char> +{ +public: + explicit string_slice () : array_slice<const char> () {} + explicit string_slice (const char *str) : array_slice (str, strlen (str)) {} + explicit string_slice (const char *str, size_t len) : array_slice (str, len) + {} + + bool operator== (string_slice other) const + { + if (!is_valid () || !other.is_valid ()) + return false; + if (size() != other.size()) + return false; + return memcmp (begin (), other.begin (), size()) == 0; + } + + static string_slice invalid () + { + return string_slice (nullptr, ~0U); + } + + /* strtok_slice is used to split a string by some deliminator into + strtok_slice's. Similarly to the posix strtok_r.but without modifying the + input string. */ + static string_slice strtok (string_slice *str, string_slice delims); + /* Removes white space from the front and back of the string_slice. */ + string_slice strip (); +}; + #endif // GCC_VEC_H