On 06/01/10 04:23, Vasily Chekalkin wrote:
Nick Wellnhofer wrote:
It seems that all ways to iterate over the characters in a UTF8 string
have quadratic running time. See the attached test. I would expect
that for keyed access and 'substr' but iterator access and 'split'
should have better performance. I had a look at the string iterator
PMC code and it doesn't use the iterators that the underlying string
API provides.

I can offer to write a patch to fix this if noone else is working on
this.

Good idea! Patches welcome!

Here is a preliminary patch.

I would also suggest to move the iterator function pointers from struct string_iterator_t to struct encoding_t and introduce new macros similar to ENCODING_ITER_INIT like I did in my patch. If that's OK I can convert the rest of the string iterator users.

It would also be helpful to remove the const qualifier from the 'str' member of struct string_iterator_t. Or is it important?

Nick
Index: src/pmc/stringiterator.pmc
===================================================================
--- src/pmc/stringiterator.pmc  (revision 43399)
+++ src/pmc/stringiterator.pmc  (working copy)
@@ -23,11 +23,10 @@
 
 
 pmclass StringIterator auto_attrs extends Iterator {
-    ATTR PMC    *string;    /* String to iterate over */
-    ATTR INTVAL  pos;       /* Current position of iterator for forward 
iterator */
-                            /* Previous position of iterator for reverse 
iterator */
-    ATTR INTVAL  length;    /* Length of C<string> */
-    ATTR INTVAL  reverse;   /* Direction of iteration. 1 - for reverse 
iteration */
+    ATTR PMC         *string;    /* String to iterate over */
+    ATTR String_iter  iter;      /* String iterator */
+    ATTR UINTVAL      length;    /* Length of C<string> */
+    ATTR INTVAL       reverse;   /* Direction of iteration. 1 - for reverse 
iteration */
 
 /*
 
@@ -39,7 +38,12 @@
 
 */
     VTABLE void init_pmc(PMC *string) {
+        Parrot_StringIterator_attributes * const attrs =
+                PARROT_STRINGITERATOR(SELF);
+        STRING * const str_val = VTABLE_get_string(INTERP, string);
+
         SET_ATTR_string(INTERP, SELF, string);
+        ENCODING_ITER_INIT(INTERP, str_val, &attrs->iter);
 
         /* by default, iterate from start */
         SELF.set_integer_native(ITERATE_FROM_START);
@@ -77,7 +81,7 @@
         Parrot_StringIterator_attributes * const clone_attrs =
                 PARROT_STRINGITERATOR(clone);
 
-        clone_attrs->pos     = attrs->pos;
+        clone_attrs->iter    = attrs->iter;
         clone_attrs->reverse = attrs->reverse;
         return clone;
     }
@@ -110,9 +114,9 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
         if (attrs->reverse)
-            return attrs->pos;
+            return attrs->iter.charpos;
         else
-            return attrs->length - attrs->pos;
+            return attrs->length - attrs->iter.charpos;
     }
 
     VTABLE INTVAL get_integer() {
@@ -137,13 +141,13 @@
                 PARROT_STRINGITERATOR(SELF);
         if (value == ITERATE_FROM_START) {
             attrs->reverse   = 0;
-            attrs->pos       = 0;
             attrs->length    = VTABLE_elements(INTERP, attrs->string);
+            ENCODING_ITER_SET_POSITION(INTERP, &attrs->iter, 0);
         }
         else if (value == ITERATE_FROM_END) {
             attrs->reverse   = 1;
-            attrs->pos       = attrs->length
-                             = VTABLE_elements(INTERP, attrs->string);
+            attrs->length    = VTABLE_elements(INTERP, attrs->string);
+            ENCODING_ITER_SET_POSITION(INTERP, &attrs->iter, attrs->length);
         }
         else
             Parrot_ex_throw_from_c_args(INTERP, NULL, 
EXCEPTION_INVALID_OPERATION,
@@ -179,14 +183,16 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
         PMC *ret;
+        STRING *str;
 
-        if (attrs->pos >= attrs->length)
+        if (attrs->iter.charpos >= attrs->length)
             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                 "StopIteration");
 
         ret = pmc_new(INTERP, Parrot_get_ctx_HLL_type(interp, 
enum_class_String));
-        VTABLE_set_string_native(INTERP, ret,
-                VTABLE_get_string_keyed_int(INTERP, attrs->string, 
attrs->pos++));
+        str = Parrot_str_iter_get_and_advance(interp,
+                VTABLE_get_string(INTERP, attrs->string), &attrs->iter);
+        VTABLE_set_string_native(INTERP, ret, str);
         return ret;
     }
 
@@ -203,11 +209,12 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
 
-        if (attrs->pos >= attrs->length)
+        if (attrs->iter.charpos >= attrs->length)
             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                 "StopIteration");
 
-        return VTABLE_get_string_keyed_int(INTERP, attrs->string, 
attrs->pos++);
+        return Parrot_str_iter_get_and_advance(interp,
+                VTABLE_get_string(INTERP, attrs->string), &attrs->iter);
     }
 
 /*
@@ -223,11 +230,11 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
 
-        if (attrs->pos >= attrs->length)
+        if (attrs->iter.charpos >= attrs->length)
             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                 "StopIteration");
 
-        return VTABLE_get_integer_keyed_int(INTERP, attrs->string, 
attrs->pos++);
+        return ENCODING_ITER_GET_AND_ADVANCE(INTERP, &attrs->iter);
     }
 
 /*
@@ -243,14 +250,16 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
         PMC *ret;
+        STRING * str;
 
-        if (!STATICSELF.get_bool())
+        if (attrs->iter.charpos <= 0)
             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                 "StopIteration");
 
         ret = pmc_new(INTERP, Parrot_get_ctx_HLL_type(interp, 
enum_class_String));
-        VTABLE_set_string_native(INTERP, ret,
-                VTABLE_get_string_keyed_int(INTERP, attrs->string, 
--attrs->pos));
+        str = Parrot_str_iter_regress_and_get(interp,
+                VTABLE_get_string(INTERP, attrs->string), &attrs->iter);
+        VTABLE_set_string_native(INTERP, ret, str);
         return ret;
     }
 
@@ -267,11 +276,12 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
 
-        if (!STATICSELF.get_bool())
+        if (attrs->iter.charpos <= 0)
             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                 "StopIteration");
 
-        return VTABLE_get_string_keyed_int(INTERP, attrs->string, 
--attrs->pos);
+        return Parrot_str_iter_regress_and_get(interp,
+                VTABLE_get_string(INTERP, attrs->string), &attrs->iter);
     }
 
 /*
@@ -287,11 +297,11 @@
         Parrot_StringIterator_attributes * const attrs =
                 PARROT_STRINGITERATOR(SELF);
 
-        if (!STATICSELF.get_bool())
+        if (attrs->iter.charpos <= 0)
             Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_OUT_OF_BOUNDS,
                 "StopIteration");
 
-        return VTABLE_get_integer_keyed_int(INTERP, attrs->string, 
--attrs->pos);
+        return ENCODING_ITER_REGRESS_AND_GET(INTERP, &attrs->iter);
     }
 
 /*
@@ -306,7 +316,7 @@
 
     VTABLE INTVAL get_integer_keyed_int(INTVAL idx) {
         return VTABLE_get_integer_keyed_int(INTERP, STATICSELF.get_pmc(),
-                PARROT_STRINGITERATOR(SELF)->pos + idx);
+                PARROT_STRINGITERATOR(SELF)->iter.charpos + idx);
     }
 
 /*
@@ -321,7 +331,7 @@
 
     VTABLE STRING *get_string_keyed_int(INTVAL idx) {
         return VTABLE_get_string_keyed_int(INTERP, STATICSELF.get_pmc(),
-                PARROT_STRINGITERATOR(SELF)->pos + idx);
+                PARROT_STRINGITERATOR(SELF)->iter.charpos + idx);
     }
 }
 
Index: src/string/encoding/utf16.c
===================================================================
--- src/string/encoding/utf16.c (revision 43399)
+++ src/string/encoding/utf16.c (working copy)
@@ -154,6 +154,13 @@
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*i);
 
+PARROT_WARN_UNUSED_RESULT
+static UINTVAL utf16_regress_and_decode(PARROT_INTERP,
+    ARGMOD(String_iter *i))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*i);
+
 static void utf16_encode_and_advance(PARROT_INTERP,
     ARGMOD(String_iter *i),
     UINTVAL c)
@@ -220,6 +227,9 @@
 #define ASSERT_ARGS_utf16_decode_and_advance __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(i))
+#define ASSERT_ARGS_utf16_regress_and_decode __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(i))
 #define ASSERT_ARGS_utf16_encode_and_advance __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(i))
@@ -734,6 +744,33 @@
 
 /*
 
+=item C<static UINTVAL utf16_regress_and_decode(PARROT_INTERP, String_iter *i)>
+
+Moves the string iterator C<i> to the previous UTF-16 codepoint.
+
+=cut
+
+*/
+
+PARROT_WARN_UNUSED_RESULT
+static UINTVAL
+utf16_regress_and_decode(PARROT_INTERP, ARGMOD(String_iter *i))
+{
+    ASSERT_ARGS(utf16_regress_and_decode)
+    UChar *s = (UChar*) i->str->strstart;
+    UINTVAL c, pos;
+    pos = i->bytepos / sizeof (UChar);
+    /* TODO either make sure that we don't go past end or use SAFE
+     *      iter versions
+     */
+    U16_PREV_UNSAFE(s, pos, c);
+    i->charpos--;
+    i->bytepos = pos * sizeof (UChar);
+    return c;
+}
+
+/*
+
 =item C<static void utf16_encode_and_advance(PARROT_INTERP, String_iter *i,
 UINTVAL c)>
 
@@ -843,6 +880,10 @@
         codepoints,
         bytes,
         iter_init,
+        utf16_decode_and_advance,
+        utf16_encode_and_advance,
+        utf16_regress_and_decode,
+        utf16_set_position,
         find_cclass
     };
     STRUCT_COPY_FROM_STRUCT(return_encoding, base_encoding);
Index: src/string/encoding/fixed_8.c
===================================================================
--- src/string/encoding/fixed_8.c       (revision 43399)
+++ src/string/encoding/fixed_8.c       (working copy)
@@ -50,6 +50,11 @@
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*iter);
 
+static UINTVAL fixed8_get_prev(PARROT_INTERP, ARGMOD(String_iter *iter))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*iter);
+
 static void fixed8_set_next(PARROT_INTERP,
     ARGMOD(String_iter *iter),
     UINTVAL c)
@@ -181,6 +186,9 @@
 #define ASSERT_ARGS_fixed8_get_next __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(iter))
+#define ASSERT_ARGS_fixed8_get_prev __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(iter))
 #define ASSERT_ARGS_fixed8_set_next __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(iter))
@@ -600,6 +608,24 @@
 
 /*
 
+=item C<static UINTVAL fixed8_get_prev(PARROT_INTERP, String_iter *iter)>
+
+Moves the string iterator C<i> to the previous codepoint.
+
+=cut
+
+*/
+
+static UINTVAL
+fixed8_get_prev(PARROT_INTERP, ARGMOD(String_iter *iter))
+{
+    ASSERT_ARGS(fixed8_get_prev)
+    iter->bytepos--;
+    return get_byte(interp, iter->str, --iter->charpos);
+}
+
+/*
+
 =item C<static void fixed8_set_next(PARROT_INTERP, String_iter *iter, UINTVAL
 c)>
 
@@ -695,6 +721,10 @@
         codepoints,
         bytes,
         iter_init,
+        fixed8_get_next,
+        fixed8_set_next,
+        fixed8_get_prev,
+        fixed8_set_position,
         find_cclass
 
     };
Index: src/string/encoding/utf8.c
===================================================================
--- src/string/encoding/utf8.c  (revision 43399)
+++ src/string/encoding/utf8.c  (working copy)
@@ -158,6 +158,12 @@
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*i);
 
+static UINTVAL utf8_regress_and_decode(PARROT_INTERP,
+    ARGMOD(String_iter *i))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*i);
+
 PARROT_CANNOT_RETURN_NULL
 static void * utf8_encode(PARROT_INTERP, ARGIN(void *ptr), UINTVAL c)
         __attribute__nonnull__(1)
@@ -238,6 +244,9 @@
 #define ASSERT_ARGS_utf8_decode_and_advance __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(i))
+#define ASSERT_ARGS_utf8_regress_and_decode __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(i))
 #define ASSERT_ARGS_utf8_encode __attribute__unused__ int _ASSERT_ARGS_CHECK = 
(\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(ptr))
@@ -505,6 +514,30 @@
 
 /*
 
+=item C<static UINTVAL utf8_regress_and_decode(PARROT_INTERP, String_iter *i)>
+
+The UTF-8 implementation of the string iterator's C<regress_and_get>
+function.
+
+=cut
+
+*/
+
+static UINTVAL
+utf8_regress_and_decode(PARROT_INTERP, ARGMOD(String_iter *i))
+{
+    ASSERT_ARGS(utf8_regress_and_decode)
+    const utf8_t *u8ptr = (utf8_t *)((char *)i->str->strstart + i->bytepos);
+
+    u8ptr--;
+    while (UTF8_IS_CONTINUATION(*u8ptr))
+        u8ptr--;
+
+    return utf8_decode(interp, u8ptr);
+}
+
+/*
+
 =item C<static void utf8_encode_and_advance(PARROT_INTERP, String_iter *i,
 UINTVAL c)>
 
@@ -1055,6 +1088,10 @@
         codepoints,
         bytes,
         iter_init,
+        utf8_decode_and_advance,
+        utf8_encode_and_advance,
+        utf8_regress_and_decode,
+        utf8_set_position,
         find_cclass
     };
     STRUCT_COPY_FROM_STRUCT(return_encoding, base_encoding);
Index: src/string/encoding/ucs2.c
===================================================================
--- src/string/encoding/ucs2.c  (revision 43399)
+++ src/string/encoding/ucs2.c  (working copy)
@@ -157,6 +157,12 @@
         __attribute__nonnull__(2)
         FUNC_MODIFIES(*i);
 
+static UINTVAL ucs2_regress_and_decode(PARROT_INTERP,
+    ARGMOD(String_iter *i))
+        __attribute__nonnull__(1)
+        __attribute__nonnull__(2)
+        FUNC_MODIFIES(*i);
+
 static void ucs2_encode_and_advance(PARROT_INTERP,
     ARGMOD(String_iter *i),
     UINTVAL c)
@@ -216,6 +222,9 @@
 #define ASSERT_ARGS_ucs2_decode_and_advance __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(i))
+#define ASSERT_ARGS_ucs2_regress_and_decode __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp) \
+    , PARROT_ASSERT_ARG(i))
 #define ASSERT_ARGS_ucs2_encode_and_advance __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp) \
     , PARROT_ASSERT_ARG(i))
@@ -611,6 +620,41 @@
 
 /*
 
+=item C<static UINTVAL ucs2_regress_and_decode(PARROT_INTERP, String_iter *i)>
+
+Moves the string iterator C<i> to the previous UCS-2 codepoint.
+
+=cut
+
+*/
+
+static UINTVAL
+ucs2_regress_and_decode(PARROT_INTERP, ARGMOD(String_iter *i))
+{
+    ASSERT_ARGS(ucs2_regress_and_decode)
+
+#if PARROT_HAS_ICU
+    UChar * const s = (UChar*) i->str->strstart;
+    size_t pos = i->bytepos / sizeof (UChar);
+
+    /* TODO either make sure that we don't go past end or use SAFE
+     *      iter versions
+     */
+    const UChar c = s[--pos];
+    i->charpos--;
+    i->bytepos = pos * sizeof (UChar);
+    return c;
+#else
+    /* This function must never be called if compiled without ICU.
+     * See TT #557
+     */
+    PARROT_ASSERT(0);
+    return (UINTVAL)0; /* Stop the static analyzers from panicing */
+#endif
+}
+
+/*
+
 =item C<static void ucs2_encode_and_advance(PARROT_INTERP, String_iter *i,
 UINTVAL c)>
 
@@ -729,6 +773,10 @@
         codepoints,
         bytes,
         iter_init,
+        ucs2_decode_and_advance,
+        ucs2_encode_and_advance,
+        ucs2_regress_and_decode,
+        ucs2_set_position,
         find_cclass
     };
     STRUCT_COPY_FROM_STRUCT(return_encoding, base_encoding);
Index: src/string/api.c
===================================================================
--- src/string/api.c    (revision 43399)
+++ src/string/api.c    (working copy)
@@ -1254,9 +1254,74 @@
     }
 }
 
+/*
 
+=item C<STRING * Parrot_str_iter_get_and_advance(PARROT_INTERP, STRING *str,
+String_iter *iter)>
+
+Returns the character in C<str> that C<iter> points to and advances C<iter>.
+
+=cut
+
+*/
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+STRING *
+Parrot_str_iter_get_and_advance(PARROT_INTERP,
+    ARGIN(STRING *str), ARGOUT(String_iter *iter))
+{
+    ASSERT_ARGS(Parrot_str_iter_get_and_advance)
+    STRING *dest  = Parrot_str_new_COW(interp, str);
+    UINTVAL start = iter->bytepos;
+
+    ENCODING_ITER_GET_AND_ADVANCE(interp, iter);
+
+    dest->strstart = (char *)dest->strstart + start;
+    dest->bufused  = iter->bytepos - start;
+    dest->strlen   = 1;
+    dest->hashval  = 0;
+
+    return dest;
+}
+
 /*
 
+=item C<STRING * Parrot_str_iter_regress_and_get(PARROT_INTERP, STRING *str,
+String_iter *iter)>
+
+Moves C<iter> backwards and returns the character in C<str> that C<iter>
+points to.
+
+=cut
+
+*/
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+STRING *
+Parrot_str_iter_regress_and_get(PARROT_INTERP,
+    ARGIN(STRING *str), ARGOUT(String_iter *iter))
+{
+    ASSERT_ARGS(Parrot_str_iter_get_and_advance)
+    STRING *dest  = Parrot_str_new_COW(interp, str);
+    UINTVAL end = iter->bytepos;
+
+    ENCODING_ITER_REGRESS_AND_GET(interp, iter);
+
+    dest->strstart = (char *)dest->strstart + iter->bytepos;
+    dest->bufused  = end - iter->bytepos;
+    dest->strlen   = 1;
+    dest->hashval  = 0;
+
+    return dest;
+}
+
+
+/*
+
 =item C<STRING * Parrot_str_replace(PARROT_INTERP, STRING *src, INTVAL offset,
 INTVAL length, STRING *rep, STRING **d)>
 
Index: include/parrot/encoding.h
===================================================================
--- include/parrot/encoding.h   (revision 43399)
+++ include/parrot/encoding.h   (working copy)
@@ -37,6 +37,10 @@
 
 typedef void (*encoding_iter_init_t)(PARROT_INTERP, const STRING *src,
         struct string_iterator_t *);
+typedef UINTVAL (*encoding_iter_get_and_advance_t)(PARROT_INTERP, struct 
string_iterator_t *);
+typedef void (*encoding_iter_set_and_advance_t)(PARROT_INTERP, struct 
string_iterator_t *, UINTVAL);
+typedef UINTVAL (*encoding_iter_regress_and_get_t)(PARROT_INTERP, struct 
string_iterator_t *);
+typedef void (*encoding_iter_set_position_t)(PARROT_INTERP, struct 
string_iterator_t *, UINTVAL);
 
 struct _encoding {
     ARGIN(const char *name);
@@ -56,6 +60,10 @@
     encoding_codepoints_t               codepoints;
     encoding_bytes_t                    bytes;
     encoding_iter_init_t                iter_init;
+    encoding_iter_get_and_advance_t     iter_get_and_advance;
+    encoding_iter_set_and_advance_t     iter_set_and_advance;
+    encoding_iter_regress_and_get_t     iter_regress_and_get;
+    encoding_iter_set_position_t        iter_set_position;
     encoding_find_cclass_t              find_cclass;
 };
 
@@ -220,6 +228,14 @@
     ((src)->encoding)->bytes((i), (src))
 #define ENCODING_ITER_INIT(i, src, iter) \
     ((src)->encoding)->iter_init((i), (src), (iter))
+#define ENCODING_ITER_GET_AND_ADVANCE(i, iter) \
+    ((iter)->str->encoding)->iter_get_and_advance((i), (iter))
+#define ENCODING_ITER_SET_AND_ADVANCE(i, iter, c) \
+    ((iter)->str->encoding)->iter_set_and_advance((i), (iter), (c))
+#define ENCODING_ITER_REGRESS_AND_GET(i, iter) \
+    ((iter)->str->encoding)->iter_regress_and_get((i), (iter))
+#define ENCODING_ITER_SET_POSITION(i, iter, pos) \
+    ((iter)->str->encoding)->iter_set_position((i), (iter), (pos))
 #define ENCODING_FIND_CCLASS(i, src, typetable, flags, pos, end) \
     ((src)->encoding)->find_cclass((i), (src), (typetable), (flags), (pos), 
(end))
 
Index: include/parrot/string_funcs.h
===================================================================
--- include/parrot/string_funcs.h       (revision 43399)
+++ include/parrot/string_funcs.h       (working copy)
@@ -391,6 +391,24 @@
 
 PARROT_EXPORT
 PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+STRING * Parrot_str_iter_get_and_advance(PARROT_INTERP,
+    ARGIN(STRING *str),
+    ARGOUT(String_iter *iter))
+        __attribute__nonnull__(1)
+        FUNC_MODIFIES(*iter);
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
+PARROT_WARN_UNUSED_RESULT
+STRING * Parrot_str_iter_regress_and_get(PARROT_INTERP,
+    ARGIN(STRING *str),
+    ARGOUT(String_iter *iter))
+        __attribute__nonnull__(1)
+        FUNC_MODIFIES(*iter);
+
+PARROT_EXPORT
+PARROT_CANNOT_RETURN_NULL
 PARROT_MALLOC
 STRING * Parrot_str_titlecase(PARROT_INTERP, ARGIN_NULLOK(const STRING *s))
         __attribute__nonnull__(1);
@@ -660,6 +678,10 @@
        PARROT_ASSERT_ARG(interp))
 #define ASSERT_ARGS_Parrot_str_substr __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_iter_get_and_advance __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp))
+#define ASSERT_ARGS_Parrot_str_iter_regress_and_get __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
+       PARROT_ASSERT_ARG(interp))
 #define ASSERT_ARGS_Parrot_str_titlecase __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
        PARROT_ASSERT_ARG(interp))
 #define ASSERT_ARGS_Parrot_str_titlecase_inplace __attribute__unused__ int 
_ASSERT_ARGS_CHECK = (\
_______________________________________________
http://lists.parrot.org/mailman/listinfo/parrot-dev

Reply via email to