From e0d83010bdb9ce4159893fb117c7c6bf15c6cae8 Mon Sep 17 00:00:00 2001 From: Marvin Humphrey Date: Thu, 22 Oct 2015 15:24:44 +0200 Subject: [PATCH 01/14] Doc tweaks for String classes --- runtime/core/Clownfish/String.cfh | 90 +++++++++++++++---------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index e422cbf4..b49a0dfa 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -34,52 +34,52 @@ public final class Clownfish::String nickname Str size_t size; String *origin; - /** Return a new String which holds a copy of the passed-in string. - * Check for UTF-8 validity. + /** Return a String which holds a copy of the supplied UTF-8 character + * data after checking for validity. */ inert incremented String* new_from_utf8(const char *utf8, size_t size); - /** Return a new String which holds a copy of the passed-in string. No - * validity checking is performed. + /** Return a String which holds a copy of the supplied UTF-8 character + * data, skipping validity checks. */ inert incremented String* new_from_trusted_utf8(const char *utf8, size_t size); - /** Initialize the String using the passed-in string. Do not check - * validity of supplied UTF-8. + /** Initialize a String which holds a copy of the supplied UTF-8 character + * data, skipping validity checks. */ inert String* init_from_trusted_utf8(String *self, const char *utf8, size_t size); - /** Return a pointer to a new String which assumes ownership of the - * passed-in string. Check validity of supplied UTF-8. + /** Return a String which assumes ownership of the supplied buffer + * containing UTF-8 character data after checking for validity. */ inert incremented String* new_steal_utf8(char *utf8, size_t size); - /** Return a pointer to a new String which assumes ownership of the - * passed-in string. Do not check validity of supplied UTF-8. + /** Return a String which assumes ownership of the supplied buffer + * containing UTF-8 character data, skipping validity checks. */ inert incremented String* new_steal_trusted_utf8(char *utf8, size_t size); - /** Initialize the String using the passed-in string. Do not check - * validity of supplied UTF-8. + /** Initialize a String which assumes ownership of the supplied buffer + * containing UTF-8 character data, skipping validity checks. */ public inert String* init_steal_trusted_utf8(String *self, char *utf8, size_t size); - /** Return a pointer to a new String which wraps an external buffer - * containing UTF-8. The buffer must stay unchanged for the lifetime - * of the String. Check validity of supplied UTF-8. + /** Return a String which wraps an external buffer containing UTF-8 + * character data after checking for validity. The buffer must stay + * unchanged for the lifetime of the String. */ inert incremented String* new_wrap_utf8(const char *utf8, size_t size); - /** Return a pointer to a new String which wraps an external buffer - * containing UTF-8. The buffer must stay unchanged for the lifetime - * of the String. Do not check validity of supplied UTF-8. + /** Return a String which wraps an external buffer containing UTF-8 + * character data, skipping validity checks. The buffer must stay + * unchanged for the lifetime of the String. */ inert incremented String* new_wrap_trusted_utf8(const char *utf8, size_t size); @@ -87,8 +87,8 @@ public final class Clownfish::String nickname Str inert incremented String* new_stack_string(void *allocation, const char *utf8, size_t size); - /** Initialize the String which wraps an external buffer containing - * UTF-8. Do not check validity of supplied UTF-8. + /** Initialize a String which wraps an external buffer containing UTF-8 + * character data after checking for validity. */ public inert String* init_wrap_trusted_utf8(String *self, const char *utf8, size_t size); @@ -98,8 +98,8 @@ public final class Clownfish::String nickname Str inert incremented String* new_from_char(int32_t code_point); - /** Return a pointer to a new String which contains formatted data - * expanded according to CB_VCatF. + /** Return a String with content expanded from a pattern and arguments + * conforming to the spec defined by CharBuf. * * Note: a user-supplied `pattern` string is a security hole * and must not be allowed. @@ -128,13 +128,14 @@ public final class Clownfish::String nickname Str incremented String* Cat(String *self, String *other); - /** Return the concatenation of the String and the passed-in raw UTF-8. + /** Return the concatenation of the String and the supplied UTF-8 + * character data after checking for validity. */ incremented String* Cat_Utf8(String *self, const char *ptr, size_t size); - /** Return the concatenation of the String and the passed-in raw UTF-8. - * Don't check for UTF-8 validity. + /** Return the concatenation of the String and the supplied UTF-8 + * character data, skipping validity checks. */ incremented String* Cat_Trusted_Utf8(String *self, const char *ptr, size_t size); @@ -157,22 +158,22 @@ public final class Clownfish::String nickname Str public double To_F64(String *self); - /** Test whether the String starts with the content of another. + /** Test whether the String starts with `prefix`. */ bool Starts_With(String *self, String *prefix); - /** Test whether the String starts with the passed-in string. + /** Test whether the String starts with `prefix`. */ bool Starts_With_Utf8(String *self, const char *prefix, size_t size); - /** Test whether the String ends with the content of another. + /** Test whether the String ends with `postfix`. */ bool Ends_With(String *self, String *postfix); - /** Test whether the String ends with the passed-in string. + /** Test whether the String ends with `postfix`. */ bool Ends_With_Utf8(String *self, const char *postfix, size_t size); @@ -186,17 +187,17 @@ public final class Clownfish::String nickname Str int64_t Find_Utf8(String *self, const char *ptr, size_t size); - /** Test whether the String matches the passed-in string. + /** Test whether the String matches the supplied UTF-8 character data. */ bool Equals_Utf8(String *self, const char *ptr, size_t size); - /** Return the number of Unicode code points in the object's string. + /** Return the number of Unicode code points the String contains. */ size_t Length(String *self); - /** Get the String's `size` attribute. + /** Return the number of bytes occupied by the String's internal content. */ size_t Get_Size(String *self); @@ -251,35 +252,33 @@ public final class Clownfish::String nickname Str String* Trim_Tail(String *self); - /** Return the Unicode code point at the specified number of code points - * in. Return 0 if the string length is exceeded. (XXX It would be + /** Return the Unicode code point located `tick` code points in from the + * top. Return 0 if out of bounds. (XXX It would be * better to throw an exception, but that's not practical with UTF-8 and * no cached length.) */ int32_t Code_Point_At(String *self, size_t tick); - /** Return the Unicode code point at the specified number of code points - * counted backwards from the end of the string. Return 0 if outside the - * string. + /** Return the Unicode code point located `tick` code points counting + * backwards from the end. Return 0 if out of bounds. */ int32_t Code_Point_From(String *self, size_t tick); - /** Return a newly allocated String containing a copy of the indicated - * substring. + /** Return a new String containing a copy of the specified substring. * @param offset Offset from the top, in code points. * @param len The desired length of the substring, in code points. */ incremented String* SubString(String *self, size_t offset, size_t len); - /** Return an iterator to the start of the string. + /** Return an iterator initialized to the start of the string. */ incremented StringIterator* Top(String *self); - /** Return an iterator to the end of the string. + /** Return an iterator initialized to the end of the string. */ incremented StringIterator* Tail(String *self); @@ -363,14 +362,12 @@ public final class Clownfish::StringIterator nickname StrIter public size_t Skip_Prev_Whitespace(StringIterator *self); - /** Test whether the content after the iterator starts with - * `prefix`. + /** Test whether the content after the iterator starts with `prefix`. */ bool Starts_With(StringIterator *self, String *prefix); - /** Test whether the content after the iterator starts with the passed-in - * string. + /** Test whether the content after the iterator starts with `prefix`. */ bool Starts_With_Utf8(StringIterator *self, const char *prefix, size_t size); @@ -381,8 +378,7 @@ public final class Clownfish::StringIterator nickname StrIter bool Ends_With(StringIterator *self, String *postfix); - /** Test whether the content before the iterator ends with the passed-in - * string. + /** Test whether the content before the iterator ends with `postfix`. */ bool Ends_With_Utf8(StringIterator *self, const char *postfix, size_t size); From f09709361e96ee018a2851f43b982c3323e0b656 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 15:27:30 +0200 Subject: [PATCH 02/14] Rename new_stack_string to init_stack_string This function doesn't allocate memory. --- runtime/core/Clownfish/String.c | 2 +- runtime/core/Clownfish/String.cfh | 6 +++--- runtime/perl/xs/XSBind.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index a1ac8751..3124be56 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -118,7 +118,7 @@ Str_new_wrap_trusted_utf8(const char *utf8, size_t size) { } String* -Str_new_stack_string(void *allocation, const char *utf8, size_t size) { +Str_init_stack_string(void *allocation, const char *utf8, size_t size) { String *self = (String*)Class_Init_Obj(STRING, allocation); return Str_init_wrap_trusted_utf8(self, utf8, size); } diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index b49a0dfa..98b341f7 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -85,7 +85,7 @@ public final class Clownfish::String nickname Str new_wrap_trusted_utf8(const char *utf8, size_t size); inert incremented String* - new_stack_string(void *allocation, const char *utf8, size_t size); + init_stack_string(void *allocation, const char *utf8, size_t size); /** Initialize a String which wraps an external buffer containing UTF-8 * character data after checking for validity. @@ -390,10 +390,10 @@ public final class Clownfish::StringIterator nickname StrIter __C__ #define CFISH_SSTR_BLANK() \ - cfish_Str_new_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), "", 0) + cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), "", 0) #define CFISH_SSTR_WRAP_UTF8(ptr, size) \ - cfish_Str_new_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), ptr, size) + cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), ptr, size) #define CFISH_STRITER_DONE -1 diff --git a/runtime/perl/xs/XSBind.c b/runtime/perl/xs/XSBind.c index 39f4c5a5..a2611521 100644 --- a/runtime/perl/xs/XSBind.c +++ b/runtime/perl/xs/XSBind.c @@ -200,7 +200,7 @@ S_maybe_perl_to_cfish(pTHX_ SV *sv, cfish_Class *klass, bool increment, if (!allocation) { CFISH_THROW(CFISH_ERR, "Allocation for stack string missing"); } - *obj_ptr = (cfish_Obj*)cfish_Str_new_stack_string( + *obj_ptr = (cfish_Obj*)cfish_Str_init_stack_string( allocation, ptr, size); return true; } From 0bfc3498832d06468a337fe554de52ab631ebf6e Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 15:36:53 +0200 Subject: [PATCH 03/14] Remove Str_Swap_Chars Only used once in Lucy. A method to replace whole strings would be more useful. --- runtime/core/Clownfish/String.c | 16 ---------------- runtime/core/Clownfish/String.cfh | 7 ------- runtime/core/Clownfish/Test/TestString.c | 14 +------------- runtime/go/build.go | 1 - runtime/go/clownfish/clownfish.go | 7 ------- runtime/go/clownfish/string_test.go | 8 -------- 6 files changed, 1 insertion(+), 52 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index 3124be56..dd6d5dd5 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -219,22 +219,6 @@ Str_To_String_IMP(String *self) { return (String*)INCREF(self); } -String* -Str_Swap_Chars_IMP(String *self, int32_t match, int32_t replacement) { - CharBuf *charbuf = CB_new(self->size); - StringIterator *iter = STACK_ITER(self, 0); - int32_t code_point; - - while (STRITER_DONE != (code_point = StrIter_Next(iter))) { - if (code_point == match) { code_point = replacement; } - CB_Cat_Char(charbuf, code_point); - } - - String *retval = CB_Yield_String(charbuf); - DECREF(charbuf); - return retval; -} - int64_t Str_To_I64_IMP(String *self) { return Str_BaseX_To_I64(self, 10); diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index 98b341f7..55b56a46 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -140,13 +140,6 @@ public final class Clownfish::String nickname Str incremented String* Cat_Trusted_Utf8(String *self, const char *ptr, size_t size); - /** Replace all instances of one character for the other. - * - * @return a new String with the characters replaced. - */ - incremented String* - Swap_Chars(String *self, int32_t match, int32_t replacement); - public int64_t To_I64(String *self); diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index d305a007..d3cc38c6 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -385,17 +385,6 @@ test_Compare_To(TestBatchRunner *runner) { DECREF(abc); } -static void -test_Swap_Chars(TestBatchRunner *runner) { - String *source = S_get_str("aXXbXc"); - String *got = Str_Swap_Chars(source, 'X', smiley_cp); - String *wanted = Str_newf("a%s%sb%sc", smiley, smiley, smiley); - TEST_TRUE(runner, Str_Equals(got, (Obj*)wanted), "Swap_Chars"); - DECREF(wanted); - DECREF(got); - DECREF(source); -} - static void test_Starts_Ends_With(TestBatchRunner *runner) { String *prefix = S_get_str("pre" SMILEY "fix_"); @@ -687,7 +676,7 @@ test_iterator_substring(TestBatchRunner *runner) { void TestStr_Run_IMP(TestString *self, TestBatchRunner *runner) { - TestBatchRunner_Plan(runner, (TestBatch*)self, 134); + TestBatchRunner_Plan(runner, (TestBatch*)self, 133); test_new(runner); test_Cat(runner); test_Clone(runner); @@ -701,7 +690,6 @@ TestStr_Run_IMP(TestString *self, TestBatchRunner *runner) { test_To_Utf8(runner); test_Length(runner); test_Compare_To(runner); - test_Swap_Chars(runner); test_Starts_Ends_With(runner); test_Get_Ptr8(runner); test_iterator(runner); diff --git a/runtime/go/build.go b/runtime/go/build.go index abc57a81..6b0b0be9 100644 --- a/runtime/go/build.go +++ b/runtime/go/build.go @@ -153,7 +153,6 @@ func specMethods(parcel *cfc.Parcel) { stringBinding := cfc.NewGoClass(parcel, "Clownfish::String") stringBinding.SpecMethod("Code_Point_At", "CodePointAt(uintptr) rune") stringBinding.SpecMethod("Code_Point_From", "CodePointFrom(uintptr) rune") - stringBinding.SpecMethod("Swap_Chars", "SwapChars(rune, rune) string") stringBinding.Register() stringIterBinding := cfc.NewGoClass(parcel, "Clownfish::StringIterator") diff --git a/runtime/go/clownfish/clownfish.go b/runtime/go/clownfish/clownfish.go index a881cda9..19861093 100644 --- a/runtime/go/clownfish/clownfish.go +++ b/runtime/go/clownfish/clownfish.go @@ -655,13 +655,6 @@ func (s *StringIMP) CodePointFrom(tick uintptr) rune { return rune(retvalCF) } -func (s *StringIMP) SwapChars(match, replacement rune) string { - self := ((*C.cfish_String)(Unwrap(s, "s"))) - retvalCF := C.CFISH_Str_Swap_Chars(self, C.int32_t(match), C.int32_t(replacement)) - defer C.cfish_dec_refcount(unsafe.Pointer(retvalCF)) - return CFStringToGo(unsafe.Pointer(retvalCF)) -} - func NewBoolean(val bool) Boolean { if val { return WRAPBoolean(unsafe.Pointer(C.cfish_inc_refcount(unsafe.Pointer(C.CFISH_TRUE)))) diff --git a/runtime/go/clownfish/string_test.go b/runtime/go/clownfish/string_test.go index 1347b197..6f8c6410 100644 --- a/runtime/go/clownfish/string_test.go +++ b/runtime/go/clownfish/string_test.go @@ -26,14 +26,6 @@ func TestStringCat(t *testing.T) { } } -func TestStringSwapChars(t *testing.T) { - s := NewString("foo") - got := s.SwapChars('o', 'u') - if got != "fuu" { - t.Error("Expected 'fuu', got", got) - } -} - func TestStringStartsWithEndsWith(t *testing.T) { s := NewString("foobar") if !s.StartsWith("foo") { From 77d6dfd6dae2d60be0424adfa82a197c769ab95d Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 15:46:42 +0200 Subject: [PATCH 04/14] Change Code_Point_{At|From} return value Make Code_Point_{At|From} return STR_OOB (-1) instead of 0 if the tick is out of bounds. This allows to process strings containing U+0000. --- runtime/core/Clownfish/String.c | 6 +++--- runtime/core/Clownfish/String.cfh | 8 ++++---- runtime/core/Clownfish/Test/TestString.c | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index dd6d5dd5..7d0b8716 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -432,16 +432,16 @@ Str_Code_Point_At_IMP(String *self, size_t tick) { StringIterator *iter = STACK_ITER(self, 0); StrIter_Advance(iter, tick); int32_t code_point = StrIter_Next(iter); - return code_point == STRITER_DONE ? 0 : code_point; + return code_point == STRITER_DONE ? STR_OOB : code_point; } int32_t Str_Code_Point_From_IMP(String *self, size_t tick) { - if (tick == 0) { return 0; } + if (tick == 0) { return STR_OOB; } StringIterator *iter = STACK_ITER(self, self->size); StrIter_Recede(iter, tick - 1); int32_t code_point = StrIter_Prev(iter); - return code_point == STRITER_DONE ? 0 : code_point; + return code_point == STRITER_DONE ? STR_OOB : code_point; } String* diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index 55b56a46..9501970b 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -246,15 +246,13 @@ public final class Clownfish::String nickname Str Trim_Tail(String *self); /** Return the Unicode code point located `tick` code points in from the - * top. Return 0 if out of bounds. (XXX It would be - * better to throw an exception, but that's not practical with UTF-8 and - * no cached length.) + * top. Return CFISH_STR_OOB if out of bounds. */ int32_t Code_Point_At(String *self, size_t tick); /** Return the Unicode code point located `tick` code points counting - * backwards from the end. Return 0 if out of bounds. + * backwards from the end. Return CFISH_STR_OOB if out of bounds. */ int32_t Code_Point_From(String *self, size_t tick); @@ -388,11 +386,13 @@ __C__ #define CFISH_SSTR_WRAP_UTF8(ptr, size) \ cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), ptr, size) +#define CFISH_STR_OOB -1 #define CFISH_STRITER_DONE -1 #ifdef CFISH_USE_SHORT_NAMES #define SSTR_BLANK CFISH_SSTR_BLANK #define SSTR_WRAP_UTF8 CFISH_SSTR_WRAP_UTF8 + #define STR_OOB CFISH_STR_OOB #define STRITER_DONE CFISH_STRITER_DONE #endif __END_C__ diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index d3cc38c6..275166bf 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -194,12 +194,12 @@ test_Code_Point_At_and_From(TestBatchRunner *runner) { code_points[i], "Code_Point_From %ld", (long)from); } - TEST_INT_EQ(runner, Str_Code_Point_At(string, num_code_points), 0, + TEST_INT_EQ(runner, Str_Code_Point_At(string, num_code_points), STR_OOB, "Code_Point_At %ld", (long)num_code_points); - TEST_INT_EQ(runner, Str_Code_Point_From(string, 0), 0, + TEST_INT_EQ(runner, Str_Code_Point_From(string, 0), STR_OOB, "Code_Point_From 0"); - TEST_INT_EQ(runner, Str_Code_Point_From(string, num_code_points + 1), 0, - "Code_Point_From %ld", (long)(num_code_points + 1)); + TEST_INT_EQ(runner, Str_Code_Point_From(string, num_code_points + 1), + STR_OOB, "Code_Point_From %ld", (long)(num_code_points + 1)); DECREF(string); } From 8b5f8dbe7934c5f22372573b11acb75255541661 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 15:52:10 +0200 Subject: [PATCH 05/14] Remove Str_compare and Str_less_than --- runtime/core/Clownfish/String.c | 48 ++++++++++++------------------- runtime/core/Clownfish/String.cfh | 13 --------- 2 files changed, 18 insertions(+), 43 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index 7d0b8716..3afbdc11 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -343,8 +343,24 @@ Str_Equals_IMP(String *self, Obj *other) { int32_t Str_Compare_To_IMP(String *self, Obj *other) { - CERTIFY(other, STRING); - return Str_compare(&self, &other); + String *twin = (String*)CERTIFY(other, STRING); + size_t min_size; + int32_t tie; + + if (self->size <= twin->size) { + min_size = self->size; + tie = self->size < twin->size ? -1 : 0; + } + else { + min_size = twin->size; + tie = 1; + } + + int comparison = memcmp(self->ptr, twin->ptr, min_size); + if (comparison < 0) { return -1; } + if (comparison > 0) { return 1; } + + return tie; } bool @@ -457,34 +473,6 @@ Str_SubString_IMP(String *self, size_t offset, size_t len) { return S_new_substring(self, start_offset, size); } -int -Str_compare(const void *va, const void *vb) { - String *a = *(String**)va; - String *b = *(String**)vb; - size_t min_size; - int tie; - - if (a->size <= b->size) { - min_size = a->size; - tie = a->size < b->size ? -1 : 0; - } - else { - min_size = b->size; - tie = 1; - } - - int comparison = memcmp(a->ptr, b->ptr, min_size); - if (comparison < 0) { return -1; } - if (comparison > 0) { return 1; } - - return tie; -} - -bool -Str_less_than(const void *va, const void *vb) { - return Str_compare(va, vb) < 0 ? true : false; -} - size_t Str_Get_Size_IMP(String *self) { return self->size; diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index 9501970b..cc29b2ee 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -107,19 +107,6 @@ public final class Clownfish::String nickname Str inert incremented String* newf(const char *pattern, ...); - /** Perform lexical comparison of two Strings, with level of indirection - * set to please qsort and friends. - */ - inert int - compare(const void *va, const void *vb); - - /** Perform lexical comparison of two Strings, with level of indirection - * set to please qsort and friends, and return true if `a` is - * less than `b`. - */ - inert bool - less_than(const void *va, const void *vb); - void* To_Host(String *self); From 86a6c98560e45e37201ef14aac74c114c628378f Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 15:57:56 +0200 Subject: [PATCH 06/14] Rename Ends_With argument from postfix to suffix --- runtime/core/Clownfish/String.c | 20 ++++++++++---------- runtime/core/Clownfish/String.cfh | 16 ++++++++-------- runtime/core/Clownfish/Test/TestString.c | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index 3afbdc11..f4732bdd 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -372,15 +372,15 @@ Str_Equals_Utf8_IMP(String *self, const char *ptr, size_t size) { } bool -Str_Ends_With_IMP(String *self, String *postfix) { - return Str_Ends_With_Utf8_IMP(self, postfix->ptr, postfix->size); +Str_Ends_With_IMP(String *self, String *suffix) { + return Str_Ends_With_Utf8_IMP(self, suffix->ptr, suffix->size); } bool -Str_Ends_With_Utf8_IMP(String *self, const char *postfix, size_t postfix_len) { - if (postfix_len <= self->size) { - const char *start = self->ptr + self->size - postfix_len; - if (memcmp(start, postfix, postfix_len) == 0) { +Str_Ends_With_Utf8_IMP(String *self, const char *suffix, size_t suffix_len) { + if (suffix_len <= self->size) { + const char *start = self->ptr + self->size - suffix_len; + if (memcmp(start, suffix, suffix_len) == 0) { return true; } } @@ -803,12 +803,12 @@ StrIter_Starts_With_Utf8_IMP(StringIterator *self, const char *prefix, } bool -StrIter_Ends_With_IMP(StringIterator *self, String *postfix) { - return StrIter_Ends_With_Utf8_IMP(self, postfix->ptr, postfix->size); +StrIter_Ends_With_IMP(StringIterator *self, String *suffix) { + return StrIter_Ends_With_Utf8_IMP(self, suffix->ptr, suffix->size); } bool -StrIter_Ends_With_Utf8_IMP(StringIterator *self, const char *postfix, +StrIter_Ends_With_Utf8_IMP(StringIterator *self, const char *suffix, size_t size) { String *string = self->string; size_t byte_offset = self->byte_offset; @@ -820,7 +820,7 @@ StrIter_Ends_With_Utf8_IMP(StringIterator *self, const char *postfix, if (byte_offset < size) { return false; } - return memcmp(string->ptr + byte_offset - size, postfix, size) == 0; + return memcmp(string->ptr + byte_offset - size, suffix, size) == 0; } void diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index cc29b2ee..7a203759 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -148,15 +148,15 @@ public final class Clownfish::String nickname Str bool Starts_With_Utf8(String *self, const char *prefix, size_t size); - /** Test whether the String ends with `postfix`. + /** Test whether the String ends with `suffix`. */ bool - Ends_With(String *self, String *postfix); + Ends_With(String *self, String *suffix); - /** Test whether the String ends with `postfix`. + /** Test whether the String ends with `suffix`. */ bool - Ends_With_Utf8(String *self, const char *postfix, size_t size); + Ends_With_Utf8(String *self, const char *suffix, size_t size); /** Return the location of the substring within the String (measured in * code points), or -1 if the substring does not match. @@ -351,15 +351,15 @@ public final class Clownfish::StringIterator nickname StrIter Starts_With_Utf8(StringIterator *self, const char *prefix, size_t size); /** Test whether the content before the iterator ends with - * `postfix`. + * `suffix`. */ bool - Ends_With(StringIterator *self, String *postfix); + Ends_With(StringIterator *self, String *suffix); - /** Test whether the content before the iterator ends with `postfix`. + /** Test whether the content before the iterator ends with `suffix`. */ bool - Ends_With_Utf8(StringIterator *self, const char *postfix, size_t size); + Ends_With_Utf8(StringIterator *self, const char *suffix, size_t size); public void Destroy(StringIterator *self); diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index 275166bf..629763da 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -387,20 +387,20 @@ test_Compare_To(TestBatchRunner *runner) { static void test_Starts_Ends_With(TestBatchRunner *runner) { - String *prefix = S_get_str("pre" SMILEY "fix_"); - String *postfix = S_get_str("_post" SMILEY "fix"); - String *empty = S_get_str(""); + String *prefix = S_get_str("pre" SMILEY "fix_"); + String *suffix = S_get_str("_post" SMILEY "fix"); + String *empty = S_get_str(""); - TEST_TRUE(runner, Str_Starts_With(postfix, postfix), + TEST_TRUE(runner, Str_Starts_With(suffix, suffix), "Starts_With self returns true"); TEST_TRUE(runner, Str_Starts_With(prefix, prefix), "Ends_With self returns true"); - TEST_TRUE(runner, Str_Starts_With(postfix, empty), + TEST_TRUE(runner, Str_Starts_With(suffix, empty), "Starts_With empty string returns true"); TEST_TRUE(runner, Str_Ends_With(prefix, empty), "Ends_With empty string returns true"); - TEST_FALSE(runner, Str_Starts_With(empty, postfix), + TEST_FALSE(runner, Str_Starts_With(empty, suffix), "Empty string Starts_With returns false"); TEST_FALSE(runner, Str_Ends_With(empty, prefix), "Empty string Ends_With returns false"); @@ -410,7 +410,7 @@ test_Starts_Ends_With(TestBatchRunner *runner) { = S_get_str("pre" SMILEY "fix_string_post" SMILEY "fix"); TEST_TRUE(runner, Str_Starts_With(string, prefix), "Starts_With returns true"); - TEST_TRUE(runner, Str_Ends_With(string, postfix), + TEST_TRUE(runner, Str_Ends_With(string, suffix), "Ends_With returns true"); DECREF(string); } @@ -420,13 +420,13 @@ test_Starts_Ends_With(TestBatchRunner *runner) { = S_get_str("pre" SMILEY "fix:string:post" SMILEY "fix"); TEST_FALSE(runner, Str_Starts_With(string, prefix), "Starts_With returns false"); - TEST_FALSE(runner, Str_Ends_With(string, postfix), + TEST_FALSE(runner, Str_Ends_With(string, suffix), "Ends_With returns false"); DECREF(string); } DECREF(prefix); - DECREF(postfix); + DECREF(suffix); DECREF(empty); } From caab9f0d953f681d5bb5654eb2f9d681159f3d8f Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 16:00:31 +0200 Subject: [PATCH 07/14] Mark Str_Trim return value as incremented --- runtime/core/Clownfish/String.cfh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index 7a203759..aca056db 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -219,17 +219,17 @@ public final class Clownfish::String nickname Str /** Remove Unicode whitespace characters from both top and tail. */ - String* + incremented String* Trim(String *self); /** Remove leading Unicode whitespace. */ - String* + incremented String* Trim_Top(String *self); /** Remove trailing Unicode whitespace. */ - String* + incremented String* Trim_Tail(String *self); /** Return the Unicode code point located `tick` code points in from the From f126bf31a299968a7595512f859c1e8c7727a8cb Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Thu, 22 Oct 2015 16:05:35 +0200 Subject: [PATCH 08/14] Make most String functions and methods public --- runtime/core/Clownfish/String.cfh | 74 +++++++++++++++---------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index aca056db..b54f80d7 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -37,31 +37,31 @@ public final class Clownfish::String nickname Str /** Return a String which holds a copy of the supplied UTF-8 character * data after checking for validity. */ - inert incremented String* + public inert incremented String* new_from_utf8(const char *utf8, size_t size); /** Return a String which holds a copy of the supplied UTF-8 character * data, skipping validity checks. */ - inert incremented String* + public inert incremented String* new_from_trusted_utf8(const char *utf8, size_t size); /** Initialize a String which holds a copy of the supplied UTF-8 character * data, skipping validity checks. */ - inert String* + public inert String* init_from_trusted_utf8(String *self, const char *utf8, size_t size); /** Return a String which assumes ownership of the supplied buffer * containing UTF-8 character data after checking for validity. */ - inert incremented String* + public inert incremented String* new_steal_utf8(char *utf8, size_t size); /** Return a String which assumes ownership of the supplied buffer * containing UTF-8 character data, skipping validity checks. */ - inert incremented String* + public inert incremented String* new_steal_trusted_utf8(char *utf8, size_t size); /** Initialize a String which assumes ownership of the supplied buffer @@ -74,14 +74,14 @@ public final class Clownfish::String nickname Str * character data after checking for validity. The buffer must stay * unchanged for the lifetime of the String. */ - inert incremented String* + public inert incremented String* new_wrap_utf8(const char *utf8, size_t size); /** Return a String which wraps an external buffer containing UTF-8 * character data, skipping validity checks. The buffer must stay * unchanged for the lifetime of the String. */ - inert incremented String* + public inert incremented String* new_wrap_trusted_utf8(const char *utf8, size_t size); inert incremented String* @@ -95,7 +95,7 @@ public final class Clownfish::String nickname Str /** Return a String which holds a single character. */ - inert incremented String* + public inert incremented String* new_from_char(int32_t code_point); /** Return a String with content expanded from a pattern and arguments @@ -104,7 +104,7 @@ public final class Clownfish::String nickname Str * Note: a user-supplied `pattern` string is a security hole * and must not be allowed. */ - inert incremented String* + public inert incremented String* newf(const char *pattern, ...); void* @@ -112,19 +112,19 @@ public final class Clownfish::String nickname Str /** Return the concatenation of the String and `other`. */ - incremented String* + public incremented String* Cat(String *self, String *other); /** Return the concatenation of the String and the supplied UTF-8 * character data after checking for validity. */ - incremented String* + public incremented String* Cat_Utf8(String *self, const char *ptr, size_t size); /** Return the concatenation of the String and the supplied UTF-8 * character data, skipping validity checks. */ - incremented String* + public incremented String* Cat_Trusted_Utf8(String *self, const char *ptr, size_t size); public int64_t @@ -132,7 +132,7 @@ public final class Clownfish::String nickname Str /** Extract a 64-bit integer from a variable-base stringified version. */ - int64_t + public int64_t BaseX_To_I64(String *self, uint32_t base); public double @@ -140,58 +140,58 @@ public final class Clownfish::String nickname Str /** Test whether the String starts with `prefix`. */ - bool + public bool Starts_With(String *self, String *prefix); /** Test whether the String starts with `prefix`. */ - bool + public bool Starts_With_Utf8(String *self, const char *prefix, size_t size); /** Test whether the String ends with `suffix`. */ - bool + public bool Ends_With(String *self, String *suffix); /** Test whether the String ends with `suffix`. */ - bool + public bool Ends_With_Utf8(String *self, const char *suffix, size_t size); /** Return the location of the substring within the String (measured in * code points), or -1 if the substring does not match. */ - int64_t + public int64_t Find(String *self, String *substring); - int64_t + public int64_t Find_Utf8(String *self, const char *ptr, size_t size); /** Test whether the String matches the supplied UTF-8 character data. */ - bool + public bool Equals_Utf8(String *self, const char *ptr, size_t size); /** Return the number of Unicode code points the String contains. */ - size_t + public size_t Length(String *self); /** Return the number of bytes occupied by the String's internal content. */ - size_t + public size_t Get_Size(String *self); /** Return the internal backing array for the String if its internal * encoding is UTF-8. If it is not encoded as UTF-8 throw an exception. */ - const char* + public const char* Get_Ptr8(String *self); /** Return a NULL-terminated copy of the string data in UTF-8 encoding. * The buffer must be freed by the caller. */ - char* + public char* To_Utf8(String *self); public incremented String* @@ -219,46 +219,46 @@ public final class Clownfish::String nickname Str /** Remove Unicode whitespace characters from both top and tail. */ - incremented String* + public incremented String* Trim(String *self); /** Remove leading Unicode whitespace. */ - incremented String* + public incremented String* Trim_Top(String *self); /** Remove trailing Unicode whitespace. */ - incremented String* + public incremented String* Trim_Tail(String *self); /** Return the Unicode code point located `tick` code points in from the * top. Return CFISH_STR_OOB if out of bounds. */ - int32_t + public int32_t Code_Point_At(String *self, size_t tick); /** Return the Unicode code point located `tick` code points counting * backwards from the end. Return CFISH_STR_OOB if out of bounds. */ - int32_t + public int32_t Code_Point_From(String *self, size_t tick); /** Return a new String containing a copy of the specified substring. * @param offset Offset from the top, in code points. * @param len The desired length of the substring, in code points. */ - incremented String* + public incremented String* SubString(String *self, size_t offset, size_t len); /** Return an iterator initialized to the start of the string. */ - incremented StringIterator* + public incremented StringIterator* Top(String *self); /** Return an iterator initialized to the end of the string. */ - incremented StringIterator* + public incremented StringIterator* Tail(String *self); } @@ -275,7 +275,7 @@ public final class Clownfish::StringIterator nickname StrIter * @param top Top iterator. Use start of string if NULL. * @param tail Tail iterator. Use end of string if NULL. */ - inert incremented String* + public inert incremented String* substring(StringIterator *top, StringIterator *tail); public incremented StringIterator* @@ -342,23 +342,23 @@ public final class Clownfish::StringIterator nickname StrIter /** Test whether the content after the iterator starts with `prefix`. */ - bool + public bool Starts_With(StringIterator *self, String *prefix); /** Test whether the content after the iterator starts with `prefix`. */ - bool + public bool Starts_With_Utf8(StringIterator *self, const char *prefix, size_t size); /** Test whether the content before the iterator ends with * `suffix`. */ - bool + public bool Ends_With(StringIterator *self, String *suffix); /** Test whether the content before the iterator ends with `suffix`. */ - bool + public bool Ends_With_Utf8(StringIterator *self, const char *suffix, size_t size); public void From 2f6b2393c84402b296b3081265f64c350529e96f Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Sat, 24 Oct 2015 15:14:59 +0200 Subject: [PATCH 09/14] Rework Str_Find interface Make Str_Find return a StringIterator. Add Str_Contains. Optimize substring search to use memchr. Unfortunately, memmem isn't widely available. --- runtime/core/Clownfish/String.c | 45 +++++++++++++++++------- runtime/core/Clownfish/String.cfh | 23 +++++++++--- runtime/core/Clownfish/Test/TestObj.c | 4 +-- runtime/core/Clownfish/Test/TestString.c | 37 +++++++++++++------ runtime/go/clownfish/string_test.go | 26 ++++++++++---- 5 files changed, 101 insertions(+), 34 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index f4732bdd..090bda61 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -44,6 +44,9 @@ static void S_die_invalid_utf8(const char *text, size_t size, const char *file, int line, const char *func); +static const char* +S_memmem(String *self, const char *substring, size_t size); + static StringIterator* S_new_stack_iter(void *allocation, String *string, size_t byte_offset); @@ -388,25 +391,43 @@ Str_Ends_With_Utf8_IMP(String *self, const char *suffix, size_t suffix_len) { return false; } -int64_t +bool +Str_Contains_IMP(String *self, String *substring) { + return !!S_memmem(self, substring->ptr, substring->size); +} + +bool +Str_Contains_Utf8_IMP(String *self, const char *substring, size_t size) { + return !!S_memmem(self, substring, size); +} + +StringIterator* Str_Find_IMP(String *self, String *substring) { return Str_Find_Utf8(self, substring->ptr, substring->size); } -int64_t -Str_Find_Utf8_IMP(String *self, const char *ptr, size_t size) { - StringIterator *iter = STACK_ITER(self, 0); - int64_t location = 0; +StringIterator* +Str_Find_Utf8_IMP(String *self, const char *substring, size_t size) { + const char *ptr = S_memmem(self, substring, size); + return ptr ? StrIter_new(self, ptr - self->ptr) : NULL; +} - while (iter->byte_offset + size <= self->size) { - if (memcmp(self->ptr + iter->byte_offset, ptr, size) == 0) { - return location; - } - StrIter_Advance(iter, 1); - location++; +static const char* +S_memmem(String *self, const char *substring, size_t size) { + if (size == 0) { return self->ptr; } + if (size > self->size) { return NULL; } + + const char *ptr = self->ptr; + const char *end = ptr + self->size - size + 1; + char first_char = substring[0]; + + // Naive string search. + while (NULL != (ptr = (const char*)memchr(ptr, first_char, end - ptr))) { + if (memcmp(ptr, substring, size) == 0) { break; } + ptr++; } - return -1; + return ptr; } String* diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index b54f80d7..f675afff 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -158,13 +158,28 @@ public final class Clownfish::String nickname Str public bool Ends_With_Utf8(String *self, const char *suffix, size_t size); - /** Return the location of the substring within the String (measured in - * code points), or -1 if the substring does not match. + /** Test whether the String contains `substring`. */ - public int64_t + public bool + Contains(String *self, String *substring); + + /** Test whether the String contains `substring`. + */ + public bool + Contains_Utf8(String *self, const char *ptr, size_t size); + + /** Return a [](StringIterator) pointing to the first occurrence of the + * substring within the String, or [](@null) if the substring does not + * match. + */ + public incremented nullable StringIterator* Find(String *self, String *substring); - public int64_t + /** Return a [](StringIterator) pointing to the first occurrence of the + * substring within the String, or [](@null) if the substring does not + * match. + */ + public incremented nullable StringIterator* Find_Utf8(String *self, const char *ptr, size_t size); /** Test whether the String matches the supplied UTF-8 character data. diff --git a/runtime/core/Clownfish/Test/TestObj.c b/runtime/core/Clownfish/Test/TestObj.c index 1bac6426..b37ad71c 100644 --- a/runtime/core/Clownfish/Test/TestObj.c +++ b/runtime/core/Clownfish/Test/TestObj.c @@ -66,7 +66,7 @@ static void test_To_String(TestBatchRunner *runner) { Obj *testobj = S_new_testobj(); String *string = Obj_To_String(testobj); - TEST_TRUE(runner, Str_Find_Utf8(string, "TestObj", 7) >= 0, "To_String"); + TEST_TRUE(runner, Str_Contains_Utf8(string, "TestObj", 7), "To_String"); DECREF(string); DECREF(testobj); } @@ -123,7 +123,7 @@ S_verify_abstract_error(TestBatchRunner *runner, Err_Attempt_t routine, Err *error = Err_trap(routine, context); TEST_TRUE(runner, error != NULL && Err_is_a(error, ERR) - && Str_Find_Utf8(Err_Get_Mess(error), "bstract", 7) != -1, + && Str_Contains_Utf8(Err_Get_Mess(error), "bstract", 7), message); DECREF(error); } diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index 629763da..291aee8b 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -150,28 +150,45 @@ test_Clone(TestBatchRunner *runner) { DECREF(wanted); } +static int64_t +S_find(String *string, String *substring) { + StringIterator *iter = Str_Find(string, substring); + if (iter == NULL) { return -1; } + size_t tick = StrIter_Recede(iter, SIZE_MAX); + DECREF(iter); + return (int64_t)tick; +} + static void -test_Find(TestBatchRunner *runner) { +test_Contains_and_Find(TestBatchRunner *runner) { String *string; String *substring = S_get_str("foo"); string = S_get_str(""); - TEST_TRUE(runner, Str_Find(string, substring) == -1, "Not in empty string"); + TEST_FALSE(runner, Str_Contains(string, substring), + "Not contained in empty string"); + TEST_INT_EQ(runner, S_find(string, substring), -1, + "Not found in empty string"); DECREF(string); string = S_get_str("foo"); - TEST_TRUE(runner, Str_Find(string, substring) == 0, "Find complete string"); + TEST_TRUE(runner, Str_Contains(string, substring), + "Contained in complete string"); + TEST_INT_EQ(runner, S_find(string, substring), 0, "Find complete string"); DECREF(string); string = S_get_str("afoo"); - TEST_TRUE(runner, Str_Find(string, substring) == 1, "Find after first"); - // TODO: Enable this test when we have real substrings. - /*Str_Set_Size(string, 3); - TEST_TRUE(runner, Str_Find(string, substring) == -1, "Don't overrun");*/ + TEST_TRUE(runner, Str_Contains(string, substring), + "Contained after first"); + TEST_INT_EQ(runner, S_find(string, substring), 1, "Find after first"); + String *prefix = Str_SubString(string, 0, 3); + TEST_FALSE(runner, Str_Contains(prefix, substring), "Don't overrun"); + DECREF(prefix); DECREF(string); string = S_get_str("afood"); - TEST_TRUE(runner, Str_Find(string, substring) == 1, "Find in middle"); + TEST_TRUE(runner, Str_Contains(string, substring), "Contained in middle"); + TEST_INT_EQ(runner, S_find(string, substring), 1, "Find in middle"); DECREF(string); DECREF(substring); @@ -676,12 +693,12 @@ test_iterator_substring(TestBatchRunner *runner) { void TestStr_Run_IMP(TestString *self, TestBatchRunner *runner) { - TestBatchRunner_Plan(runner, (TestBatch*)self, 133); + TestBatchRunner_Plan(runner, (TestBatch*)self, 138); test_new(runner); test_Cat(runner); test_Clone(runner); test_Code_Point_At_and_From(runner); - test_Find(runner); + test_Contains_and_Find(runner); test_SubString(runner); test_Trim(runner); test_To_F64(runner); diff --git a/runtime/go/clownfish/string_test.go b/runtime/go/clownfish/string_test.go index 6f8c6410..fb67aae3 100644 --- a/runtime/go/clownfish/string_test.go +++ b/runtime/go/clownfish/string_test.go @@ -63,15 +63,29 @@ func TestStringBaseXToI64(t *testing.T) { } } +func TestStringContains(t *testing.T) { + s := NewString("foobarbaz") + if !s.Contains("bar") { + t.Error("Contains yes") + } + if s.Contains("banana") { + t.Error("Contains no") + } +} + func TestStringFind(t *testing.T) { s := NewString("foobarbaz") - var got int64 = s.Find("bar") - if got != 3 { - t.Error("Find yes", got) + iter := s.Find("bar") + var pos int = -1 + if iter != nil { + pos = int(iter.Recede(100)) + } + if pos != 3 { + t.Error("Find yes", pos) } - got = s.Find("banana") - if got != -1 { - t.Error("Find no", got) + iter = s.Find("banana") + if iter != nil { + t.Error("Find no") } } From 4ea023cb6faeafca6d0e89f9c9b211aa08552e9a Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Sat, 24 Oct 2015 15:38:01 +0200 Subject: [PATCH 10/14] Rename Skip_{Next|Prev}_Whitesapce Rename Skip_Next_Whitespace to Skip_Whitespace, Skip_Prev_Whitespace to Skip_Whitespace_Back. --- runtime/core/Clownfish/String.c | 12 ++++++------ runtime/core/Clownfish/String.cfh | 4 ++-- runtime/core/Clownfish/Test/TestString.c | 16 ++++++++-------- runtime/go/clownfish/string_test.go | 6 +++--- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index 090bda61..dca2b461 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -433,12 +433,12 @@ S_memmem(String *self, const char *substring, size_t size) { String* Str_Trim_IMP(String *self) { StringIterator *top = STACK_ITER(self, 0); - StrIter_Skip_Next_Whitespace(top); + StrIter_Skip_Whitespace(top); StringIterator *tail = NULL; if (top->byte_offset < self->size) { tail = STACK_ITER(self, self->size); - StrIter_Skip_Prev_Whitespace(tail); + StrIter_Skip_Whitespace_Back(tail); } return StrIter_substring((StringIterator*)top, (StringIterator*)tail); @@ -447,14 +447,14 @@ Str_Trim_IMP(String *self) { String* Str_Trim_Top_IMP(String *self) { StringIterator *top = STACK_ITER(self, 0); - StrIter_Skip_Next_Whitespace(top); + StrIter_Skip_Whitespace(top); return StrIter_substring((StringIterator*)top, NULL); } String* Str_Trim_Tail_IMP(String *self) { StringIterator *tail = STACK_ITER(self, self->size); - StrIter_Skip_Prev_Whitespace(tail); + StrIter_Skip_Whitespace_Back(tail); return StrIter_substring(NULL, (StringIterator*)tail); } @@ -771,7 +771,7 @@ StrIter_Recede_IMP(StringIterator *self, size_t num) { } size_t -StrIter_Skip_Next_Whitespace_IMP(StringIterator *self) { +StrIter_Skip_Whitespace_IMP(StringIterator *self) { size_t num_skipped = 0; size_t byte_offset = self->byte_offset; int32_t code_point; @@ -787,7 +787,7 @@ StrIter_Skip_Next_Whitespace_IMP(StringIterator *self) { } size_t -StrIter_Skip_Prev_Whitespace_IMP(StringIterator *self) { +StrIter_Skip_Whitespace_Back_IMP(StringIterator *self) { size_t num_skipped = 0; size_t byte_offset = self->byte_offset; int32_t code_point; diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index f675afff..530db6df 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -347,13 +347,13 @@ public final class Clownfish::StringIterator nickname StrIter * @return the number of code points skipped. */ public size_t - Skip_Next_Whitespace(StringIterator *self); + Skip_Whitespace(StringIterator *self); /** Skip whitespace backward. * @return the number of code points skipped. */ public size_t - Skip_Prev_Whitespace(StringIterator *self); + Skip_Whitespace_Back(StringIterator *self); /** Test whether the content after the iterator starts with `prefix`. */ diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index 291aee8b..3a115104 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -600,19 +600,19 @@ test_iterator_whitespace(TestBatchRunner *runner) { { StringIterator *iter = Str_Top(ws_smiley); - TEST_INT_EQ(runner, StrIter_Skip_Next_Whitespace(iter), num_spaces, - "Skip_Next_Whitespace"); - TEST_INT_EQ(runner, StrIter_Skip_Next_Whitespace(iter), 0, - "Skip_Next_Whitespace without whitespace"); + TEST_INT_EQ(runner, StrIter_Skip_Whitespace(iter), num_spaces, + "Skip_Whitespace"); + TEST_INT_EQ(runner, StrIter_Skip_Whitespace(iter), 0, + "Skip_Whitespace without whitespace"); DECREF(iter); } { StringIterator *iter = Str_Tail(ws_smiley); - TEST_INT_EQ(runner, StrIter_Skip_Prev_Whitespace(iter), num_spaces, - "Skip_Prev_Whitespace"); - TEST_INT_EQ(runner, StrIter_Skip_Prev_Whitespace(iter), 0, - "Skip_Prev_Whitespace without whitespace"); + TEST_INT_EQ(runner, StrIter_Skip_Whitespace_Back(iter), num_spaces, + "Skip_Whitespace_Back"); + TEST_INT_EQ(runner, StrIter_Skip_Whitespace_Back(iter), 0, + "Skip_Whitespace_Back without whitespace"); DECREF(iter); } diff --git a/runtime/go/clownfish/string_test.go b/runtime/go/clownfish/string_test.go index fb67aae3..cdfad20d 100644 --- a/runtime/go/clownfish/string_test.go +++ b/runtime/go/clownfish/string_test.go @@ -310,14 +310,14 @@ func TestStrIterStartsWithEndsWith(t *testing.T) { func TestStrIterSkipWhite(t *testing.T) { iter := NewStringIterator(NewString("foo bar"), 0) - if got := iter.SkipNextWhitespace(); got != 0 { + if got := iter.SkipWhitespace(); got != 0 { t.Error("No whitespace to skip") } iter.Advance(3) - if got := iter.SkipNextWhitespace(); got != 2 || !iter.StartsWith("bar") { + if got := iter.SkipWhitespace(); got != 2 || !iter.StartsWith("bar") { t.Error("Skip forward 2 spaces") } - if got := iter.SkipPrevWhitespace(); got != 2 || !iter.EndsWith("foo") { + if got := iter.SkipWhitespaceBack(); got != 2 || !iter.EndsWith("foo") { t.Error("Skip backwards 2 spaces") } } From 6fadaae2b17c776dac6387147b7d3d8c64f982d2 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Sat, 24 Oct 2015 16:03:23 +0200 Subject: [PATCH 11/14] New stack string macro SSTR_WRAP_C Creates a stack string from a null-terminated C string. Also useful for string literals. (strlen of a literal should be constant folded by the compiler.) --- runtime/core/Clownfish/Class.c | 4 ++-- runtime/core/Clownfish/String.cfh | 8 ++++++++ runtime/core/Clownfish/Test/TestHash.c | 12 ++++++------ runtime/core/Clownfish/Test/TestObj.c | 2 +- runtime/core/Clownfish/Test/TestVector.c | 2 +- 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/runtime/core/Clownfish/Class.c b/runtime/core/Clownfish/Class.c index 62d98411..ba83928d 100644 --- a/runtime/core/Clownfish/Class.c +++ b/runtime/core/Clownfish/Class.c @@ -206,7 +206,7 @@ Class_bootstrap(const cfish_ClassSpec *specs, size_t num_specs, // Only store novel methods for now. for (size_t i = 0; i < spec->num_novel_meths; ++i) { const NovelMethSpec *mspec = &novel_specs[num_novel++]; - String *name = SSTR_WRAP_UTF8(mspec->name, strlen(mspec->name)); + String *name = SSTR_WRAP_C(mspec->name); Method *method = Method_new(name, mspec->callback_func, *mspec->offset); klass->methods[i] = method; @@ -406,7 +406,7 @@ Class_Add_Host_Method_Alias_IMP(Class *self, const char *alias, fprintf(stderr, "Method %s not found\n", meth_name); abort(); } - String *string = SSTR_WRAP_UTF8(alias, strlen(alias)); + String *string = SSTR_WRAP_C(alias); Method_Set_Host_Alias(method, string); } diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index 530db6df..a063095c 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -18,6 +18,9 @@ parcel Clownfish; __C__ +// For strlen +#include + // For CFISH_ALLOCA_OBJ. #include "Clownfish/Class.h" @@ -385,6 +388,10 @@ __C__ #define CFISH_SSTR_BLANK() \ cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), "", 0) +#define CFISH_SSTR_WRAP_C(ptr) \ + cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), ptr, \ + strlen(ptr)) + #define CFISH_SSTR_WRAP_UTF8(ptr, size) \ cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), ptr, size) @@ -393,6 +400,7 @@ __C__ #ifdef CFISH_USE_SHORT_NAMES #define SSTR_BLANK CFISH_SSTR_BLANK + #define SSTR_WRAP_C CFISH_SSTR_WRAP_C #define SSTR_WRAP_UTF8 CFISH_SSTR_WRAP_UTF8 #define STR_OOB CFISH_STR_OOB #define STRITER_DONE CFISH_STRITER_DONE diff --git a/runtime/core/Clownfish/Test/TestHash.c b/runtime/core/Clownfish/Test/TestHash.c index d3e96f2f..e3fb0d71 100644 --- a/runtime/core/Clownfish/Test/TestHash.c +++ b/runtime/core/Clownfish/Test/TestHash.c @@ -40,7 +40,7 @@ static void test_Equals(TestBatchRunner *runner) { Hash *hash = Hash_new(0); Hash *other = Hash_new(0); - String *stuff = SSTR_WRAP_UTF8("stuff", 5); + String *stuff = SSTR_WRAP_C("stuff"); TEST_TRUE(runner, Hash_Equals(hash, (Obj*)other), "Empty hashes are equal"); @@ -68,9 +68,9 @@ test_Store_and_Fetch(TestBatchRunner *runner) { const size_t starting_cap = Hash_Get_Capacity(hash); Vector *expected = Vec_new(100); Vector *got = Vec_new(100); - String *twenty = SSTR_WRAP_UTF8("20", 2); - String *forty = SSTR_WRAP_UTF8("40", 2); - String *foo = SSTR_WRAP_UTF8("foo", 3); + String *twenty = SSTR_WRAP_C("20"); + String *forty = SSTR_WRAP_C("40"); + String *foo = SSTR_WRAP_C("foo"); for (int32_t i = 0; i < 100; i++) { String *str = Str_newf("%i32", i); @@ -161,8 +161,8 @@ test_Keys_Values(TestBatchRunner *runner) { Vec_Clear(values); { - String *forty = SSTR_WRAP_UTF8("40", 2); - String *nope = SSTR_WRAP_UTF8("nope", 4); + String *forty = SSTR_WRAP_C("40"); + String *nope = SSTR_WRAP_C("nope"); TEST_TRUE(runner, Hash_Has_Key(hash, forty), "Has_Key"); TEST_FALSE(runner, Hash_Has_Key(hash, nope), "Has_Key returns false for non-existent key"); diff --git a/runtime/core/Clownfish/Test/TestObj.c b/runtime/core/Clownfish/Test/TestObj.c index b37ad71c..6f9b6957 100644 --- a/runtime/core/Clownfish/Test/TestObj.c +++ b/runtime/core/Clownfish/Test/TestObj.c @@ -36,7 +36,7 @@ TestObj_new() { static Obj* S_new_testobj() { - String *class_name = SSTR_WRAP_UTF8("TestObj", 7); + String *class_name = SSTR_WRAP_C("TestObj"); Obj *obj; Class *klass = Class_fetch_class(class_name); if (!klass) { diff --git a/runtime/core/Clownfish/Test/TestVector.c b/runtime/core/Clownfish/Test/TestVector.c index 30621f04..71835040 100644 --- a/runtime/core/Clownfish/Test/TestVector.c +++ b/runtime/core/Clownfish/Test/TestVector.c @@ -61,7 +61,7 @@ static void test_Equals(TestBatchRunner *runner) { Vector *array = Vec_new(0); Vector *other = Vec_new(0); - String *stuff = SSTR_WRAP_UTF8("stuff", 5); + String *stuff = SSTR_WRAP_C("stuff"); TEST_TRUE(runner, Vec_Equals(array, (Obj*)array), "Array equal to self"); From 16a4270cdcde34085954e2a26f29f03c3bb2ec0d Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Sat, 24 Oct 2015 16:58:51 +0200 Subject: [PATCH 12/14] Return STR_OOB if string iterator is done Returning STR_OOB instead of STRITER_DONE aligns better with Code_Point_At. --- runtime/core/Clownfish/String.c | 18 ++++++++---------- runtime/core/Clownfish/String.cfh | 6 ++---- runtime/core/Clownfish/Test/TestString.c | 4 ++-- runtime/go/ext/clownfish.c | 2 +- runtime/perl/xs/XSBind.c | 2 +- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index dca2b461..db270016 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -200,7 +200,7 @@ Str_Hash_Sum_IMP(String *self) { const StrIter_Next_t next = METHOD_PTR(STRINGITERATOR, CFISH_StrIter_Next); int32_t code_point; - while (STRITER_DONE != (code_point = next(iter))) { + while (STR_OOB != (code_point = next(iter))) { hashvalue = ((hashvalue << 5) + hashvalue) ^ code_point; } @@ -241,7 +241,7 @@ Str_BaseX_To_I64_IMP(String *self, uint32_t base) { } // Accumulate. - while (code_point != STRITER_DONE) { + while (code_point != STR_OOB) { if (isalnum(code_point)) { int32_t addend = isdigit(code_point) ? code_point - '0' @@ -468,8 +468,7 @@ int32_t Str_Code_Point_At_IMP(String *self, size_t tick) { StringIterator *iter = STACK_ITER(self, 0); StrIter_Advance(iter, tick); - int32_t code_point = StrIter_Next(iter); - return code_point == STRITER_DONE ? STR_OOB : code_point; + return StrIter_Next(iter); } int32_t @@ -477,8 +476,7 @@ Str_Code_Point_From_IMP(String *self, size_t tick) { if (tick == 0) { return STR_OOB; } StringIterator *iter = STACK_ITER(self, self->size); StrIter_Recede(iter, tick - 1); - int32_t code_point = StrIter_Prev(iter); - return code_point == STRITER_DONE ? STR_OOB : code_point; + return StrIter_Prev(iter); } String* @@ -628,7 +626,7 @@ StrIter_Next_IMP(StringIterator *self) { size_t byte_offset = self->byte_offset; size_t size = string->size; - if (byte_offset >= size) { return STRITER_DONE; } + if (byte_offset >= size) { return STR_OOB; } const uint8_t *const ptr = (const uint8_t*)string->ptr; int32_t retval = ptr[byte_offset++]; @@ -681,7 +679,7 @@ int32_t StrIter_Prev_IMP(StringIterator *self) { size_t byte_offset = self->byte_offset; - if (byte_offset == 0) { return STRITER_DONE; } + if (byte_offset == 0) { return STR_OOB; } const uint8_t *const ptr = (const uint8_t*)self->string->ptr; int32_t retval = ptr[--byte_offset]; @@ -776,7 +774,7 @@ StrIter_Skip_Whitespace_IMP(StringIterator *self) { size_t byte_offset = self->byte_offset; int32_t code_point; - while (STRITER_DONE != (code_point = StrIter_Next(self))) { + while (STR_OOB != (code_point = StrIter_Next(self))) { if (!StrHelp_is_whitespace(code_point)) { break; } byte_offset = self->byte_offset; ++num_skipped; @@ -792,7 +790,7 @@ StrIter_Skip_Whitespace_Back_IMP(StringIterator *self) { size_t byte_offset = self->byte_offset; int32_t code_point; - while (STRITER_DONE != (code_point = StrIter_Prev(self))) { + while (STR_OOB != (code_point = StrIter_Prev(self))) { if (!StrHelp_is_whitespace(code_point)) { break; } byte_offset = self->byte_offset; ++num_skipped; diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index a063095c..014b6a13 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -319,13 +319,13 @@ public final class Clownfish::StringIterator nickname StrIter Has_Prev(StringIterator *self); /** Return the code point after the current position and advance the - * iterator. Return CFISH_STRITER_DONE at the end of the string. + * iterator. Return CFISH_STR_OOB at the end of the string. */ public int32_t Next(StringIterator *self); /** Return the code point before the current position and go one step back. - * Return CFISH_STRITER_DONE at the start of the string. + * Return CFISH_STR_OOB at the start of the string. */ public int32_t Prev(StringIterator *self); @@ -396,14 +396,12 @@ __C__ cfish_Str_init_stack_string(CFISH_ALLOCA_OBJ(CFISH_STRING), ptr, size) #define CFISH_STR_OOB -1 -#define CFISH_STRITER_DONE -1 #ifdef CFISH_USE_SHORT_NAMES #define SSTR_BLANK CFISH_SSTR_BLANK #define SSTR_WRAP_C CFISH_SSTR_WRAP_C #define SSTR_WRAP_UTF8 CFISH_SSTR_WRAP_UTF8 #define STR_OOB CFISH_STR_OOB - #define STRITER_DONE CFISH_STRITER_DONE #endif __END_C__ diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index 3a115104..6fd5212f 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -535,7 +535,7 @@ test_iterator(TestBatchRunner *runner) { TEST_TRUE(runner, !StrIter_Has_Next(iter), "Has_Next at end of string"); - TEST_INT_EQ(runner, StrIter_Next(iter), STRITER_DONE, + TEST_INT_EQ(runner, StrIter_Next(iter), STR_OOB, "Next at end of string"); StringIterator *tail = Str_Tail(string); @@ -556,7 +556,7 @@ test_iterator(TestBatchRunner *runner) { TEST_TRUE(runner, !StrIter_Has_Prev(iter), "Has_Prev at end of string"); - TEST_INT_EQ(runner, StrIter_Prev(iter), STRITER_DONE, + TEST_INT_EQ(runner, StrIter_Prev(iter), STR_OOB, "Prev at start of string"); StringIterator *top = Str_Top(string); diff --git a/runtime/go/ext/clownfish.c b/runtime/go/ext/clownfish.c index 5d8c01ff..884fe0e4 100644 --- a/runtime/go/ext/clownfish.c +++ b/runtime/go/ext/clownfish.c @@ -182,7 +182,7 @@ Method_Host_Name_IMP(Method *self) { StringIterator *iter = StrIter_new(self->name, 0); CharBuf *charbuf = CB_new(Str_Get_Size(self->name)); int32_t code_point; - while (STRITER_DONE != (code_point = StrIter_Next(iter))) { + while (STR_OOB != (code_point = StrIter_Next(iter))) { if (code_point != '_') { CB_Cat_Char(charbuf, code_point); } diff --git a/runtime/perl/xs/XSBind.c b/runtime/perl/xs/XSBind.c index a2611521..83007ab5 100644 --- a/runtime/perl/xs/XSBind.c +++ b/runtime/perl/xs/XSBind.c @@ -782,7 +782,7 @@ CFISH_Method_Host_Name_IMP(cfish_Method *self) { cfish_CharBuf *buf = cfish_CB_new(CFISH_Str_Get_Size(name)); cfish_StringIterator *iter = CFISH_Str_Top(name); int32_t code_point; - while (CFISH_STRITER_DONE != (code_point = CFISH_StrIter_Next(iter))) { + while (CFISH_STR_OOB != (code_point = CFISH_StrIter_Next(iter))) { if (code_point > 127) { THROW(CFISH_ERR, "Can't lowercase '%o'", name); } From 43312f9de56eef53d5d67a26203e5d06f8e9e824 Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Sat, 24 Oct 2015 18:16:47 +0200 Subject: [PATCH 13/14] Remove StrIter assertions With immutable strings, the StrIter byte offset should always be within bounds. --- runtime/core/Clownfish/String.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index db270016..1e28f08a 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -555,10 +555,6 @@ StrIter_substring(StringIterator *top, StringIterator *tail) { } tail_offset = tail->byte_offset; - if (tail_offset > string->size) { - THROW(ERR, "Invalid StringIterator offset"); - UNREACHABLE_RETURN(String*); - } } if (top == NULL) { @@ -811,11 +807,6 @@ StrIter_Starts_With_Utf8_IMP(StringIterator *self, const char *prefix, String *string = self->string; size_t byte_offset = self->byte_offset; - if (byte_offset > string->size) { - THROW(ERR, "Invalid StringIterator offset"); - UNREACHABLE_RETURN(bool); - } - if (string->size - byte_offset < size) { return false; } return memcmp(string->ptr + byte_offset, prefix, size) == 0; @@ -832,11 +823,6 @@ StrIter_Ends_With_Utf8_IMP(StringIterator *self, const char *suffix, String *string = self->string; size_t byte_offset = self->byte_offset; - if (byte_offset > string->size) { - THROW(ERR, "Invalid StringIterator offset"); - UNREACHABLE_RETURN(bool); - } - if (byte_offset < size) { return false; } return memcmp(string->ptr + byte_offset - size, suffix, size) == 0; From 795778809346ae2cb0743fd543c5f1338d1b308e Mon Sep 17 00:00:00 2001 From: Nick Wellnhofer Date: Wed, 28 Oct 2015 15:38:33 +0100 Subject: [PATCH 14/14] Rename StrIter_substring to StrIter_crop --- runtime/core/Clownfish/String.c | 14 +++++++------- runtime/core/Clownfish/String.cfh | 6 +++--- runtime/core/Clownfish/Test/TestString.c | 16 ++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/runtime/core/Clownfish/String.c b/runtime/core/Clownfish/String.c index 1e28f08a..4daaa83c 100644 --- a/runtime/core/Clownfish/String.c +++ b/runtime/core/Clownfish/String.c @@ -441,21 +441,21 @@ Str_Trim_IMP(String *self) { StrIter_Skip_Whitespace_Back(tail); } - return StrIter_substring((StringIterator*)top, (StringIterator*)tail); + return StrIter_crop((StringIterator*)top, (StringIterator*)tail); } String* Str_Trim_Top_IMP(String *self) { StringIterator *top = STACK_ITER(self, 0); StrIter_Skip_Whitespace(top); - return StrIter_substring((StringIterator*)top, NULL); + return StrIter_crop((StringIterator*)top, NULL); } String* Str_Trim_Tail_IMP(String *self) { StringIterator *tail = STACK_ITER(self, self->size); StrIter_Skip_Whitespace_Back(tail); - return StrIter_substring(NULL, (StringIterator*)tail); + return StrIter_crop(NULL, (StringIterator*)tail); } size_t @@ -534,14 +534,14 @@ S_new_stack_iter(void *allocation, String *string, size_t byte_offset) { } String* -StrIter_substring(StringIterator *top, StringIterator *tail) { +StrIter_crop(StringIterator *top, StringIterator *tail) { String *string; size_t top_offset; size_t tail_offset; if (tail == NULL) { if (top == NULL) { - THROW(ERR, "StrIter_substring: Both top and tail are NULL"); + THROW(ERR, "StrIter_crop: Both top and tail are NULL"); UNREACHABLE_RETURN(String*); } string = top->string; @@ -550,7 +550,7 @@ StrIter_substring(StringIterator *top, StringIterator *tail) { else { string = tail->string; if (top != NULL && string != top->string) { - THROW(ERR, "StrIter_substring: strings don't match"); + THROW(ERR, "StrIter_crop: strings don't match"); UNREACHABLE_RETURN(String*); } @@ -563,7 +563,7 @@ StrIter_substring(StringIterator *top, StringIterator *tail) { else { top_offset = top->byte_offset; if (top_offset > tail_offset) { - THROW(ERR, "StrIter_substring: top is behind tail"); + THROW(ERR, "StrIter_crop: top is behind tail"); UNREACHABLE_RETURN(String*); } } diff --git a/runtime/core/Clownfish/String.cfh b/runtime/core/Clownfish/String.cfh index 014b6a13..284e6ec9 100644 --- a/runtime/core/Clownfish/String.cfh +++ b/runtime/core/Clownfish/String.cfh @@ -290,11 +290,11 @@ public final class Clownfish::StringIterator nickname StrIter new(String *string, size_t byte_offset); /** Return the substring between the top and tail iterators. - * @param top Top iterator. Use start of string if NULL. - * @param tail Tail iterator. Use end of string if NULL. + * @param top Top iterator. Use start of string if [](@null). + * @param tail Tail iterator. Use end of string if [](@null). */ public inert incremented String* - substring(StringIterator *top, StringIterator *tail); + crop(StringIterator *top, StringIterator *tail); public incremented StringIterator* Clone(StringIterator *self); diff --git a/runtime/core/Clownfish/Test/TestString.c b/runtime/core/Clownfish/Test/TestString.c index 6fd5212f..c32dbd1a 100644 --- a/runtime/core/Clownfish/Test/TestString.c +++ b/runtime/core/Clownfish/Test/TestString.c @@ -627,9 +627,9 @@ test_iterator_substring(TestBatchRunner *runner) { StringIterator *end = Str_Tail(string); { - String *substring = StrIter_substring(start, end); + String *substring = StrIter_crop(start, end); TEST_TRUE(runner, Str_Equals(substring, (Obj*)string), - "StrIter_substring whole string"); + "StrIter_crop whole string"); DECREF(substring); } @@ -637,10 +637,10 @@ test_iterator_substring(TestBatchRunner *runner) { StrIter_Recede(end, 2); { - String *substring = StrIter_substring(start, end); + String *substring = StrIter_crop(start, end); String *wanted = Str_newf("b%sc", smiley); TEST_TRUE(runner, Str_Equals(substring, (Obj*)wanted), - "StrIter_substring"); + "StrIter_crop"); TEST_TRUE(runner, StrIter_Starts_With(start, wanted), "Starts_With returns true"); @@ -669,19 +669,19 @@ test_iterator_substring(TestBatchRunner *runner) { } { - String *substring = StrIter_substring(end, NULL); + String *substring = StrIter_crop(end, NULL); String *wanted = Str_newf("%sd", smiley); TEST_TRUE(runner, Str_Equals(substring, (Obj*)wanted), - "StrIter_substring with NULL tail"); + "StrIter_crop with NULL tail"); DECREF(wanted); DECREF(substring); } { - String *substring = StrIter_substring(NULL, start); + String *substring = StrIter_crop(NULL, start); String *wanted = Str_newf("a%s", smiley); TEST_TRUE(runner, Str_Equals(substring, (Obj*)wanted), - "StrIter_substring with NULL top"); + "StrIter_crop with NULL top"); DECREF(wanted); DECREF(substring); }