Commits

Carl Friedrich Bolz committed 34888d6

an implementation of string_replace in rstring

Comments (0)

Files changed (2)

rpython/rlib/rstring.py

     res.reverse()
     return res
 
+def string_replace(input, sub, by, maxsplit=-1):
+    if maxsplit == 0:
+        return input
+
+    if not sub:
+        upper = len(input)
+        if maxsplit > 0 and maxsplit < upper + 2:
+            upper = maxsplit - 1
+            assert upper >= 0
+
+        try:
+            result_size = ovfcheck(upper * len(by))
+            result_size = ovfcheck(result_size + upper)
+            result_size = ovfcheck(result_size + len(by))
+            remaining_size = len(input) - upper
+            result_size = ovfcheck(result_size + remaining_size)
+        except OverflowError:
+            raise
+        builder = StringBuilder(result_size)
+        for i in range(upper):
+            builder.append(by)
+            builder.append(input[i])
+        builder.append(by)
+        builder.append_slice(input, upper, len(input))
+    else:
+        # First compute the exact result size
+        count = input.count(sub)
+        if count > maxsplit and maxsplit > 0:
+            count = maxsplit
+        diff_len = len(by) - len(sub)
+        try:
+            result_size = ovfcheck(diff_len * count)
+            result_size = ovfcheck(result_size + len(input))
+        except OverflowError:
+            raise
+
+        builder = StringBuilder(result_size)
+        start = 0
+        sublen = len(sub)
+
+        while maxsplit != 0:
+            next = input.find(sub, start)
+            if next < 0:
+                break
+            builder.append_slice(input, start, next)
+            builder.append(by)
+            start = next + sublen
+            maxsplit -= 1   # NB. if it's already < 0, it stays < 0
+
+        builder.append_slice(input, start, len(input))
+
+    return builder.build()
+
 # -------------- public API ---------------------------------
 
 INIT_SIZE = 100 # XXX tweak

rpython/rlib/test/test_rstring.py

 import sys, py
 
 from rpython.rlib.rstring import StringBuilder, UnicodeBuilder, split, rsplit
+from rpython.rlib.rstring import string_replace
 from rpython.rtyper.test.tool import BaseRtypingTest, LLRtypeMixin
 
 def test_split():
     assert rsplit(u'endcase test', u'test') == [u'endcase ', u'']
     py.test.raises(ValueError, rsplit, u"abc", u'')
 
+def test_string_replace():
+    assert string_replace('one!two!three!', '!', '@', 1) == 'one@two!three!'
+    assert string_replace('one!two!three!', '!', '') == 'onetwothree'
+    assert string_replace('one!two!three!', '!', '@', 2) == 'one@two@three!'
+    assert string_replace('one!two!three!', '!', '@', 3) == 'one@two@three@'
+    assert string_replace('one!two!three!', '!', '@', 4) == 'one@two@three@'
+    assert string_replace('one!two!three!', '!', '@', 0) == 'one!two!three!'
+    assert string_replace('one!two!three!', '!', '@') == 'one@two@three@'
+    assert string_replace('one!two!three!', 'x', '@') == 'one!two!three!'
+    assert string_replace('one!two!three!', 'x', '@', 2) == 'one!two!three!'
+    assert string_replace('abc', '', '-') == '-a-b-c-'
+    assert string_replace('abc', '', '-', 3) == '-a-b-c'
+    assert string_replace('abc', '', '-', 0) == 'abc'
+    assert string_replace('', '', '') == ''
+    assert string_replace('', '', 'a') == 'a'
+    assert string_replace('abc', 'ab', '--', 0) == 'abc'
+    assert string_replace('abc', 'xy', '--') == 'abc'
+    assert string_replace('123', '123', '') == ''
+    assert string_replace('123123', '123', '') == ''
+    assert string_replace('123x123', '123', '') == 'x'
+
+def test_string_replace_overflow():
+    if sys.maxint > 2**31-1:
+        py.test.skip("Wrong platform")
+    s = "a" * (2**16)
+    with py.test.raises(OverflowError):
+        string_replace(s, "", s)
+    with py.test.raises(OverflowError):
+        string_replace(s, "a", s)
+    with py.test.raises(OverflowError):
+        string_replace(s, "a", s, len(s) - 10)
+
+
 def test_string_builder():
     s = StringBuilder()
     s.append("a")
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.