[pypy-svn] r39705 - in pypy/branch/rope-branch/pypy: config objspace/std objspace/std/test

cfbolz at codespeak.net cfbolz at codespeak.net
Fri Mar 2 16:24:31 CET 2007


Author: cfbolz
Date: Fri Mar  2 16:24:27 2007
New Revision: 39705

Added:
   pypy/branch/rope-branch/pypy/objspace/std/rope.py
   pypy/branch/rope-branch/pypy/objspace/std/ropeobject.py
   pypy/branch/rope-branch/pypy/objspace/std/test/test_rope.py
   pypy/branch/rope-branch/pypy/objspace/std/test/test_ropeobject.py
Modified:
   pypy/branch/rope-branch/pypy/config/pypyoption.py
   pypy/branch/rope-branch/pypy/objspace/std/marshal_impl.py
   pypy/branch/rope-branch/pypy/objspace/std/model.py
   pypy/branch/rope-branch/pypy/objspace/std/objspace.py
   pypy/branch/rope-branch/pypy/objspace/std/stringtype.py
   pypy/branch/rope-branch/pypy/objspace/std/unicodeobject.py
Log:
a first go at implementing ropes.


Modified: pypy/branch/rope-branch/pypy/config/pypyoption.py
==============================================================================
--- pypy/branch/rope-branch/pypy/config/pypyoption.py	(original)
+++ pypy/branch/rope-branch/pypy/config/pypyoption.py	Fri Mar  2 16:24:27 2007
@@ -123,6 +123,9 @@
         BoolOption("withstrslice", "use strings optimized for slicing",
                    default=False),
 
+        BoolOption("withrope", "use ropes as the string implementation",
+                   default=False),
+
         BoolOption("withstrdict",
                    "use dictionaries optimized for string keys",
                    default=False),

Modified: pypy/branch/rope-branch/pypy/objspace/std/marshal_impl.py
==============================================================================
--- pypy/branch/rope-branch/pypy/objspace/std/marshal_impl.py	(original)
+++ pypy/branch/rope-branch/pypy/objspace/std/marshal_impl.py	Fri Mar  2 16:24:27 2007
@@ -303,11 +303,11 @@
         m.atom_str(TYPE_STRING, s)
 
 def unmarshal_String(space, u, tc):
-    return W_StringObject(u.get_str())
+    return space.wrap(u.get_str())
 register(TYPE_STRING, unmarshal_String)
 
 def unmarshal_interned(space, u, tc):
-    w_ret = W_StringObject(u.get_str())
+    w_ret = space.wrap(u.get_str())
     u.stringtable_w.append(w_ret)
     w_intern = space.builtin.get('intern')
     space.call_function(w_intern, w_ret)
@@ -495,7 +495,7 @@
 string_to_buffer = app.interphook('string_to_buffer')
 
 def unmarshal_buffer(space, u, tc):
-    w_s = W_StringObject(u.get_str())
+    w_s = space.wrap(u.get_str())
     return string_to_buffer(space, w_s)
 register(TYPE_UNKNOWN, unmarshal_buffer)
 

Modified: pypy/branch/rope-branch/pypy/objspace/std/model.py
==============================================================================
--- pypy/branch/rope-branch/pypy/objspace/std/model.py	(original)
+++ pypy/branch/rope-branch/pypy/objspace/std/model.py	Fri Mar  2 16:24:27 2007
@@ -17,6 +17,8 @@
     "withmultidict"  : ["dictmultiobject.W_DictMultiObject",
                         "dictmultiobject.W_DictMultiIterObject"],
     "withmultilist"  : ["listmultiobject.W_ListMultiObject"],
+    "withrope"       : ["ropeobject.W_RopeObject",
+                        "ropeobject.W_RopeIterObject"],
     "withrangelist"  : ["rangeobject.W_RangeListObject",
                         "rangeobject.W_RangeIterObject"],
     "withtproxy" : ["proxyobject.W_TransparentList",
@@ -68,6 +70,7 @@
         from pypy.objspace.std import dictmultiobject
         from pypy.objspace.std import listmultiobject
         from pypy.objspace.std import stringobject
+        from pypy.objspace.std import ropeobject
         from pypy.objspace.std import strsliceobject
         from pypy.objspace.std import strjoinobject
         from pypy.objspace.std import typeobject
@@ -115,6 +118,7 @@
             dictobject.W_DictObject: True,
             dictobject.W_DictIterObject: True,
             listobject.W_ListObject: True,
+            stringobject.W_StringObject: True,
         }
         for option, value in config.objspace.std:
             if option.startswith("with") and option in option_to_typename:
@@ -132,6 +136,8 @@
 
         if config.objspace.std.withmultilist:
             del self.typeorder[listobject.W_ListObject]
+        if config.objspace.std.withrope:
+            del self.typeorder[stringobject.W_StringObject]
 
         #check if we missed implementations
         from pypy.objspace.std.objspace import _registered_implementations
@@ -179,9 +185,14 @@
             (complexobject.W_ComplexObject, 
                     complexobject.delegate_Float2Complex),
             ]
-        self.typeorder[stringobject.W_StringObject] += [
-         (unicodeobject.W_UnicodeObject, unicodeobject.delegate_String2Unicode),
-            ]
+        if not config.objspace.std.withrope:
+            self.typeorder[stringobject.W_StringObject] += [
+             (unicodeobject.W_UnicodeObject, unicodeobject.delegate_String2Unicode),
+                ]
+        else:
+            self.typeorder[ropeobject.W_RopeObject] += [
+             (unicodeobject.W_UnicodeObject, unicodeobject.delegate_String2Unicode),
+                ]
 
         if config.objspace.std.withstrslice:
             self.typeorder[strsliceobject.W_StringSliceObject] += [

Modified: pypy/branch/rope-branch/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/branch/rope-branch/pypy/objspace/std/objspace.py	(original)
+++ pypy/branch/rope-branch/pypy/objspace/std/objspace.py	Fri Mar  2 16:24:27 2007
@@ -335,6 +335,9 @@
             else:
                 return self.newint(x)
         if isinstance(x, str):
+            if self.config.objspace.std.withrope:
+                from pypy.objspace.std.ropeobject import rope, W_RopeObject
+                return W_RopeObject(rope.LiteralStringNode(x))
             return W_StringObject(x)
         if isinstance(x, unicode):
             return W_UnicodeObject([unichr(ord(u)) for u in x]) # xxx
@@ -468,7 +471,11 @@
         except ValueError:  # chr(out-of-range)
             raise OperationError(self.w_ValueError,
                                  self.wrap("character code not in range(256)"))
-        return W_StringObject(''.join(chars))
+        if self.config.objspace.std.withrope:
+            from pypy.objspace.std.ropeobject import W_RopeObject, rope
+            return W_RopeObject(rope.rope_from_charlist(chars))
+        else:
+            return W_StringObject(''.join(chars))
 
     def newunicode(self, chars):
         try:

Added: pypy/branch/rope-branch/pypy/objspace/std/rope.py
==============================================================================
--- (empty file)
+++ pypy/branch/rope-branch/pypy/objspace/std/rope.py	Fri Mar  2 16:24:27 2007
@@ -0,0 +1,782 @@
+import py
+import sys
+
+NEW_NODE_WHEN_LENGTH = 16
+MAX_DEPTH = 32 # maybe should be smaller
+MIN_SLICE_LENGTH = 64
+CONCATENATE_WHEN_MULTIPLYING = 128
+
+def find_fib_index(l):
+    if l == 0:
+        return -1
+    a, b = 1, 2
+    i = 0
+    while 1:
+        if a <= l < b:
+            return i
+        a, b = b, a + b
+        i += 1
+
+class StringNode(object):
+    def length(self):
+        return 0
+
+    def depth(self):
+        return 0
+
+    def rebalance(self):
+        return self
+
+    def flatten(self):
+        return ''
+
+    def __add__(self, other):
+        return concatenate(self, other)
+    
+    def __getitem__(self, index):
+        if isinstance(index, slice):
+            start, stop, step = index.indices(self.length())
+            # XXX sucks
+            slicelength = len(range(start, stop, step))
+            return getslice(self, start, stop, step, slicelength)
+        return self.getitem(index)
+
+    def getitem(self, index):
+        raise NotImplementedError("abstract base class")
+
+    def getitem_slice(self, start, stop):
+        # XXX really horrible, in most cases
+        result = []
+        for i in range(start, stop):
+            result.append(self.getitem(i))
+        return rope_from_charlist(result)
+
+    def view(self):
+        from pypy.translator.tool.pygame import graphclient
+        view([self])
+
+    def check_balanced(self):
+        return True
+
+
+class LiteralStringNode(StringNode):
+    def __init__(self, s):
+        self.s = s
+    
+    def length(self):
+        return len(self.s)
+
+    def flatten(self):
+        return self.s
+
+    def getitem(self, index):
+        return self.s[index]
+
+    def getitem_slice(self, start, stop):
+        return LiteralStringNode(self.s[start:stop])
+
+    def dot(self, seen, toplevel=False):
+        if self in seen:
+            return
+        seen[self] = True
+        addinfo = str(self.s).replace('"', "'") or "_"
+        if len(addinfo) > 10:
+            addinfo = addinfo[:3] + "..." + addinfo[-3:]
+        yield ('"%s" [shape=box,label="length: %s\\n%s"];' % (
+            id(self), len(self.s),
+            repr(addinfo).replace('"', '').replace("\\", "\\\\")))
+
+
+class BinaryConcatNode(StringNode):
+    def __init__(self, left, right):
+        self.left = left
+	self.right = right
+        self.len = left.length() + right.length()
+        self._depth = max(left.depth(), right.depth()) + 1
+        self.balanced = False
+
+    def check_balanced(self):
+        if self.balanced:
+            return True
+        if not self.left.check_balanced() or not self.right.check_balanced():
+            return False
+        left = self.left
+        right = self.right
+        llen = left.length()
+        rlen = right.length()
+        ldepth = left.depth()
+        rdepth = right.depth()
+        balanced = (find_fib_index(self.len // (NEW_NODE_WHEN_LENGTH / 2)) >=
+                    self._depth)
+        self.balanced = balanced
+        return balanced
+
+    def length(self):
+        return self.len
+
+    def depth(self):
+        return self._depth
+
+    def getitem(self, index):
+        llen = self.left.length()
+        if index >= llen:
+            return self.right.getitem(index - llen)
+        else:
+            return self.left.getitem(index)
+
+    def flatten(self):
+        f = fringe(self)
+        return "".join([node.flatten() for node in f])
+ 
+    def rebalance(self):
+        return rebalance([self], self.len)
+
+    def dot(self, seen, toplevel=False):
+        if self in seen:
+            return
+        seen[self] = True
+        if toplevel:
+            addition = ", fillcolor=red"
+        elif self.check_balanced():
+            addition = ", fillcolor=yellow"
+        else:
+            addition = ""
+        yield '"%s" [shape=octagon,label="+\\ndepth=%s, length=%s"%s];' % (
+                id(self), self._depth, self.len, addition)
+        for child in [self.left, self.right]:
+            yield '"%s" -> "%s";' % (id(self), id(child))
+            for line in child.dot(seen):
+                yield line
+
+class SliceNode(StringNode):
+    def __init__(self, start, stop, node):
+        self.start = start
+	self.stop = stop
+	self.node = node
+
+    def length(self):
+        return self.stop - self.start
+
+    def getitem_slice(self, start, stop):
+        return self.node.getitem_slice(self.start + start, self.start + stop)
+
+    def getitem(self, index):
+        return self.node.getitem(self.start + index)
+
+    def flatten(self):
+        return self.node.flatten()[self.start: self.stop]
+
+    def dot(self, seen, toplevel=False):
+        if self in seen:
+            return
+        seen[self] = True
+        yield '"%s" [shape=octagon,label="slice\\nstart=%s, stop=%s"];' % (
+                id(self), self.start, self.stop)
+        yield '"%s" -> "%s";' % (id(self), id(self.node))
+        for line in self.node.dot(seen):
+            yield line
+
+def concatenate(node1, node2):
+    if node1.length() == 0:
+        return node2
+    if node2.length() == 0:
+        return node1
+    if (isinstance(node2, LiteralStringNode) and
+        len(node2.s) <= NEW_NODE_WHEN_LENGTH):
+        if isinstance(node1, LiteralStringNode):
+            if len(node1.s) + len(node2.s) <= NEW_NODE_WHEN_LENGTH:
+                return LiteralStringNode(node1.s + node2.s)
+        elif isinstance(node1, BinaryConcatNode):
+            r = node1.right
+            if isinstance(r, LiteralStringNode):
+                if len(r.s) + len(node2.s) <= NEW_NODE_WHEN_LENGTH:
+                    return BinaryConcatNode(node1.left,
+                                            LiteralStringNode(r.s + node2.s))
+    result = BinaryConcatNode(node1, node2)
+    if result.depth() > MAX_DEPTH: #XXX better check
+        return result.rebalance()
+    return result
+
+def getslice(node, start, stop, step, slicelength):
+    start, stop, node = find_straddling(node, start, stop)
+    if step != 1:
+        # XXX optimize later using SeekableCharIterator
+        iter = SeekableCharIterator(node)
+        iter.seekforward(start)
+        result = [iter.next()]
+        for i in range(slicelength - 1):
+            iter.seekforward(step - 1)
+            result.append(iter.next())
+        return rope_from_charlist(result)
+    return getslice_one(node, start, stop)
+
+def getslice_one(node, start, stop):
+    if isinstance(node, BinaryConcatNode):
+        if start == 0:
+            if stop == node.length():
+                return node
+            return getslice_left(node, stop)
+        if stop == node.length():
+            return getslice_right(node, start)
+        return concatenate(
+            getslice_right(node.left, start),
+            getslice_left(node.right, stop - node.left.length()))
+    else:
+        return getslice_primitive(node, start, stop)
+
+def find_straddling(node, start, stop):
+    while 1:
+        if isinstance(node, BinaryConcatNode):
+            llen = node.left.length()
+            if start >= llen:
+                node = node.right
+                start = start - llen
+                stop = stop - llen
+                continue
+            if stop <= llen:
+                node = node.left
+                continue
+        return start, stop, node
+
+def getslice_right(node, start):
+    while 1:
+        if start == 0:
+            return node
+        if isinstance(node, BinaryConcatNode):
+            llen = node.left.length()
+            if start >= llen:
+                node = node.right
+                start = start - llen
+                continue
+            else:
+                return concatenate(getslice_right(node.left, start),
+                                   node.right)
+        return getslice_primitive(node, start, node.length())
+
+def getslice_left(node, stop):
+    while 1:
+        if stop == node.length():
+            return node
+        if isinstance(node, BinaryConcatNode):
+            llen = node.left.length()
+            if stop <= llen:
+                node = node.left
+                continue
+            else:
+                return concatenate(node.left,
+                                   getslice_left(node.right, stop - llen))
+        return getslice_primitive(node, 0, stop)
+
+
+def getslice_primitive(node, start, stop):
+    if stop - start >= MIN_SLICE_LENGTH:
+        if isinstance(node, SliceNode):
+            return SliceNode(start + node.start, stop + node.start,
+                             node.node)
+        return SliceNode(start, stop, node)
+    return node.getitem_slice(start, stop)
+
+def multiply(node, times):
+    if times <= 0:
+        return LiteralStringNode("")
+    if times == 1:
+        return node
+    end_length = node.length() * times
+    num_bits = 0
+    mask = times
+    while mask:
+        num_bits += 1
+        mask >>= 1
+    result = node
+    mask = 1 << (num_bits - 2)
+    #import pdb; pdb.set_trace()
+    for i in range(num_bits - 1):
+        if mask & times:
+            if result.length() < CONCATENATE_WHEN_MULTIPLYING:
+                result = concatenate(result, result)
+                result = concatenate(result, node)
+            else:
+                result = BinaryConcatNode(result, result)
+                result = BinaryConcatNode(result, node)
+        else:
+            if result.length() < CONCATENATE_WHEN_MULTIPLYING:
+                result = concatenate(result, result)
+            else:
+                result = BinaryConcatNode(result, result)
+        mask >>= 1
+    return result
+
+def join(node, l):
+    if node.length() == 0:
+        return rebalance(l)
+    nodelist = [None] * (2 * len(l) - 1)
+    length = 0
+    for i in range(len(l)):
+        nodelist[2 * i] = l[i]
+        length += l[i].length()
+    for i in range(len(l) - 1):
+        nodelist[2 * i + 1] = node
+    length += (len(l) - 1) * node.length()
+    return rebalance(nodelist, length)
+
+def rebalance(nodelist, sizehint=-1):
+    if not nodelist:
+        return LiteralStringNode("")
+    nodelist.reverse()
+    if sizehint < 0:
+        sizehint = 0
+        for node in nodelist:
+            sizehint += node.length()
+    l = [None] * (find_fib_index(sizehint) + 2)
+    stack = nodelist
+    i = 0
+    curr = None
+    while stack:
+        curr = stack.pop()
+        while 1:
+            if isinstance(curr, BinaryConcatNode) and not curr.balanced:
+                stack.append(curr.right)
+                curr = curr.left
+            else:
+                i = orig_i = find_fib_index(curr.length())
+                index = 0
+                added = False
+                while index <= i:
+                    if l[index] is not None:
+                        curr = concatenate(l[index], curr)
+                        l[index] = None
+                        if index >= orig_i or not added:
+                            i += 1
+                            added = True
+                    index += 1
+                if i == len(l):
+                    return curr
+                l[i] = curr
+                break
+    for index in range(i + 1, len(l)):
+        if l[index] is not None:
+            curr = BinaryConcatNode(l[index], curr)
+    assert curr is not None
+    curr.check_balanced()
+    return curr
+
+# __________________________________________________________________________
+# construction from normal strings
+
+def rope_from_charlist(charlist):
+    nodelist = []
+    size = 0
+    for i in range(0, len(charlist), NEW_NODE_WHEN_LENGTH):
+        chars = charlist[i: min(len(charlist), i + NEW_NODE_WHEN_LENGTH)]
+        nodelist.append(LiteralStringNode("".join(chars)))
+        size += len(chars)
+    return rebalance(nodelist, size)
+
+# __________________________________________________________________________
+# searching
+
+def find_char(node, c, start=0, stop=-1):
+    offset = 0
+    length = node.length()
+    if stop == -1:
+        stop = length
+    if start != 0 or stop != length:
+        newstart, newstop, node = find_straddling(node, start, stop)
+        offset = start - newstart
+        start = newstart
+        stop = newstop
+    if isinstance(node, LiteralStringNode):
+        result = node.s.find(c, start, stop)
+        if result == -1:
+            return -1
+        return result + offset
+    elif isinstance(node, SliceNode):
+        return find_char(node.node, c, node.start + start,
+                         node.start + stop) - node.start + offset
+    iter = CharIterator(node)
+    i = 0
+    while i < stop:
+        try:
+            c2 = iter.next()
+            if i < start:
+                i += 1
+                continue
+            if c == c2:
+                return i + offset
+            i += 1
+        except StopIteration:
+            return -1
+    return -1
+
+def find(node, subnode, start=0, stop=-1):
+
+    len1 = node.length()
+    if stop > len1 or stop == -1:
+        stop = len1
+    substring = subnode.flatten() # stressful to do it as a node
+    len2 = len(substring)
+    if len2 == 1:
+        return find_char(node, substring[0], start, stop)
+    if len2 == 0:
+        if (stop - start) < 0:
+            return -1
+        return start
+    restart = construct_restart_positions(substring)
+    return _find(node, substring, start, stop, restart)
+
+def _find(node, substring, start, stop, restart):
+    len2 = len(substring)
+    i = 0
+    m = start
+    iter = SeekableCharIterator(node)
+    iter.seekforward(start)
+    c = iter.next()
+    while m + i < stop:
+        if c == substring[i]:
+            i += 1
+            if i == len2:
+                return m
+            if m + i < stop:
+                c = iter.next()
+        else:
+            # mismatch, go back to the last possible starting pos
+            if i==0:
+                m += 1
+                if m + i < stop:
+                    c = iter.next()
+            else:
+                e = restart[i-1]
+                new_m = m + i - e
+                assert new_m <= m + i
+                seek = m + i - new_m
+                if seek:
+                    iter.seekback(m + i - new_m)
+                    c = iter.next()
+                m = new_m
+                i = e
+    return -1
+
+def construct_restart_positions(s):
+    l = len(s)
+    restart = [0] * l
+    restart[0] = 0
+    i = 1
+    j = 0
+    while i < l:
+        if s[i] == s[j]:
+            j += 1
+            restart[i] = j
+            i += 1
+        elif j>0:
+            j = restart[j-1]
+        else:
+            restart[i] = 0
+            i += 1
+            j = 0
+    return restart
+
+def construct_restart_positions_node(node):
+    # really a bit overkill
+    l = node.length()
+    restart = [0] * l
+    restart[0] = 0
+    i = 1
+    j = 0
+    iter1 = CharIterator(node)
+    iter1.next()
+    c1 = iter1.next()
+    iter2 = SeekableCharIterator(node)
+    c2 = iter2.next()
+    while i < l:
+        if c1 == c2:
+            j += 1
+            if j != l:
+                c2 = iter2.next()
+            restart[i] = j
+            i += 1
+            if i != l:
+                c1 = iter1.next()
+            else:
+                break
+        elif j>0:
+            new_j = restart[j-1]
+            assert new_j < j
+            iter2.seekback(j - new_j)
+            c2 = iter2.next()
+            j = new_j
+        else:
+            restart[i] = 0
+            i += 1
+            if i != l:
+                c1 = iter1.next()
+            else:
+                break
+            j = 0
+            iter2 = SeekableCharIterator(node)
+            c2 = iter2.next()
+    return restart
+
+def view(objs):
+    from pypy.translator.tool.pygame import graphclient
+    content = ["digraph G{"]
+    seen = {}
+    for i, obj in enumerate(objs):
+        if obj is None:
+            content.append(str(i) + ";")
+        else:
+            content.extend(obj.dot(seen, toplevel=True))
+    content.append("}")
+    p = py.test.ensuretemp("automaton").join("temp.dot")
+    p.write("\n".join(content))
+    graphclient.display_dot_file(str(p))
+
+
+# __________________________________________________________________________
+# iteration
+
+class FringeIterator(object):
+    def __init__(self, node):
+        self.stack = [node]
+	
+    def next(self):
+        while self.stack:
+            curr = self.stack.pop()
+            while 1:
+                if isinstance(curr, BinaryConcatNode):
+                    self.stack.append(curr.right)
+                    curr = curr.left
+                else:
+                    return curr
+        raise StopIteration
+
+def fringe(node):
+    result = []
+    iter = FringeIterator(node)
+    while 1:
+        try:
+            result.append(iter.next())
+        except StopIteration:
+            return result
+
+class SeekableFringeIterator(object):
+    def __init__(self, node):
+        self.stack = [node]
+        self.fringestack = []
+        self.fringe = []
+	
+    def next(self):
+        if self.fringestack:
+            result = self.fringestack.pop()
+            self.fringe.append(result)
+            return result
+        while self.stack:
+            curr = self.stack.pop()
+            while 1:
+                if isinstance(curr, BinaryConcatNode):
+                    self.stack.append(curr.right)
+                    curr = curr.left
+                else:
+                    self.fringe.append(curr)
+                    return curr
+        raise StopIteration
+
+    def seekback(self):
+        result = self.fringe.pop()
+        self.fringestack.append(result)
+        return result
+
+
+class CharIterator(object):
+    def __init__(self, node):
+        self.iter = FringeIterator(node)
+        self.node = None
+        self.nodelength = 0
+        self.index = 0
+
+    def next(self):
+        node = self.node
+        if node is None:
+            while 1:
+                node = self.node = self.iter.next()
+                nodelength = self.nodelength = node.length()
+                if nodelength != 0:
+                    break
+            self.index = 0
+        index = self.index
+        result = self.node.getitem(index)
+        if self.index == self.nodelength - 1:
+            self.node = None
+        else:
+            self.index = index + 1
+        return result
+
+class SeekableCharIterator(object):
+    def __init__(self, node):
+        self.iter = SeekableFringeIterator(node)
+        self.node = self.nextnode()
+        self.nodelength = self.node.length()
+        self.index = 0
+
+    def nextnode(self):
+        while 1:
+            node = self.node = self.iter.next()
+            nodelength = self.nodelength = node.length()
+            if nodelength != 0:
+                break
+        self.index = 0
+        return node
+
+    def next(self):
+        node = self.node
+        if node is None:
+            node = self.nextnode()
+        index = self.index
+        result = self.node.getitem(index)
+        if self.index == self.nodelength - 1:
+            self.node = None
+        self.index = index + 1
+        return result
+
+    def seekforward(self, numchars):
+        if numchars < (self.nodelength - self.index):
+            self.index += numchars
+            return
+        numchars -= self.nodelength - self.index
+        while 1:
+            node = self.iter.next()
+            length = node.length()
+            if length <= numchars:
+                numchars -= length
+            else:
+                self.index = numchars
+                self.node = node
+                self.nodelength = node.length()
+                return
+        
+    def seekback(self, numchars):
+        if numchars <= self.index:
+            self.index -= numchars
+            return
+        numchars -= self.index
+        self.iter.seekback() # for first item
+        while 1:
+            node = self.iter.seekback()
+            length = node.length()
+            if length < numchars:
+                numchars -= length
+            else:
+                self.index = length - numchars
+                self.node = self.iter.next()
+                self.nodelength = self.node.length()
+                return
+
+class FindIterator(object):
+    def __init__(self, node, sub, start=0, stop=-1):
+        self.node = node
+        len1 = self.length = node.length()
+        substring = self.substring = sub.flatten() # for now
+        len2 = len(substring)
+        self.search_length = len2
+        if len2 == 0:
+            self.restart_positions = None
+        elif len2 == 1:
+            self.restart_positions = None
+        else:
+            self.restart_positions = construct_restart_positions(substring)
+        self.start = start
+        if stop == -1 or stop > len1:
+            stop = len1
+        self.stop = stop
+    
+    def next(self):
+        if self.search_length == 0:
+            if (self.stop - self.start) < 0:
+                raise StopIteration
+            start = self.start
+            self.start += 1
+            return start
+        elif self.search_length == 1:
+            result = find_char(self.node, self.substring[0],
+                               self.start, self.stop)
+            if result == -1:
+                self.start = self.length
+                raise StopIteration
+            self.start = result + 1
+            return result
+        if self.start >= self.stop:
+            raise StopIteration
+        result = _find(self.node, self.substring, self.start,
+                       self.stop, self.restart_positions)
+        if result == -1:
+            self.start = self.length
+            raise StopIteration
+        self.start = result + self.search_length
+        return result
+
+# __________________________________________________________________________
+# comparison
+
+
+def eq(node1, node2):
+    if node1 is node2:
+        return True
+    # could be cleverer and detect partial equalities
+    if node1.length() != node2.length():
+        return False
+    iter1 = CharIterator(node1)
+    iter2 = CharIterator(node2)
+    while 1:
+        try:
+            c = iter1.next()
+        except StopIteration:
+            return True
+        if c != iter2.next():
+            return False
+
+def compare(node1, node2):
+    len1 = node1.length()
+    len2 = node2.length()
+    if not len1:
+        if not len2:
+            return 0
+        return -1
+    if not len2:
+        return 1
+
+    if len1 < len2:
+        cmplen = len1
+    else:
+        cmplen = len2
+    i = 0
+    iter1 = CharIterator(node1)
+    iter2 = CharIterator(node2)
+    while i < cmplen:
+        diff = ord(iter1.next()) - ord(iter2.next())
+        if diff != 0:
+            return diff
+        i += 1
+    return len1 - len2
+
+
+# __________________________________________________________________________
+# misc
+
+
+def hash_rope(rope):
+    from pypy.rlib.rarithmetic import intmask
+    length = rope.length()
+    if length == 0:
+        x = -1
+    else:
+        x = ord(rope.getitem(0)) << 7
+        iter = CharIterator(rope)
+        while 1:
+            try:
+                x = (1000003*x) ^ ord(iter.next())
+            except StopIteration:
+                break
+        x ^= length
+        if x == 0:
+            x = -1
+    return intmask(x)
+

Added: pypy/branch/rope-branch/pypy/objspace/std/ropeobject.py
==============================================================================
--- (empty file)
+++ pypy/branch/rope-branch/pypy/objspace/std/ropeobject.py	Fri Mar  2 16:24:27 2007
@@ -0,0 +1,1034 @@
+from pypy.objspace.std.objspace import *
+from pypy.interpreter import gateway
+from pypy.rlib.objectmodel import we_are_translated
+from pypy.objspace.std.inttype import wrapint
+from pypy.objspace.std.sliceobject import W_SliceObject
+from pypy.objspace.std import slicetype
+from pypy.objspace.std.listobject import W_ListObject
+from pypy.objspace.std.noneobject import W_NoneObject
+from pypy.objspace.std.tupleobject import W_TupleObject
+from pypy.rlib.rarithmetic import ovfcheck
+
+from pypy.objspace.std import rope
+
+class W_RopeObject(W_Object):
+    from pypy.objspace.std.stringtype import str_typedef as typedef
+
+    def __init__(w_self, node):
+        w_self._node = node
+
+    def __repr__(w_self):
+        """ representation for debugging purposes """
+        return "%s(%r)" % (w_self.__class__.__name__, w_self._node)
+
+    def unwrap(w_self, space):
+        return w_self._node.flatten()
+
+    def create_if_subclassed(w_self):
+        if type(w_self) == W_RopeObject:
+            return w_self
+        return W_RopeObject(w_self._node)
+
+W_RopeObject.empty = W_RopeObject(rope.LiteralStringNode(""))
+
+registerimplementation(W_RopeObject)
+
+class W_RopeIterObject(W_Object):
+    from pypy.objspace.std.itertype import iter_typedef as typedef
+
+    def __init__(w_self, w_rope, index=0):
+        w_self.node = node = w_rope._node
+        w_self.char_iter = rope.CharIterator(node)
+        w_self.index = index
+
+registerimplementation(W_RopeIterObject)
+
+def wrap_rpystr(s):
+    return W_RopeObject(rope.LiteralStringNode(s))
+
+def _is_generic(space, w_self, fun): 
+    l = w_self._node.length()
+    if l == 0:
+        return space.w_False
+    iter = rope.CharIterator(w_self._node)
+    for i in range(l):
+        if not fun(iter.next()):
+            return space.w_False
+    return space.w_True
+_is_generic._annspecialcase_ = "specialize:arg(2)"
+
+def _upper(ch):
+    if ch.islower():
+        o = ord(ch) - 32
+        return chr(o)
+    else:
+        return ch
+    
+def _lower(ch):
+    if ch.isupper():
+        o = ord(ch) + 32
+        return chr(o)
+    else:
+        return ch
+
+_isspace = lambda c: c.isspace()
+_isdigit = lambda c: c.isdigit()
+_isalpha = lambda c: c.isalpha()
+_isalnum = lambda c: c.isalnum()
+
+def str_isspace__Rope(space, w_self):
+    return _is_generic(space, w_self, _isspace)
+
+def str_isdigit__Rope(space, w_self):
+    return _is_generic(space, w_self, _isdigit)
+
+def str_isalpha__Rope(space, w_self):
+    return _is_generic(space, w_self, _isalpha)
+
+def str_isalnum__Rope(space, w_self):
+    return _is_generic(space, w_self, _isalnum)
+
+def str_isupper__Rope(space, w_self):
+    """Return True if all cased characters in S are uppercase and there is
+at least one cased character in S, False otherwise."""
+    l = w_self._node.length()
+    
+    if l == 0:
+        return space.w_False
+    cased = False
+    iter = rope.CharIterator(w_self._node)
+    for idx in range(l):
+        c = iter.next()
+        if c.islower():
+            return space.w_False
+        elif not cased and c.isupper():
+            cased = True
+    return space.newbool(cased)
+
+def str_islower__Rope(space, w_self):
+    """Return True if all cased characters in S are lowercase and there is
+at least one cased character in S, False otherwise."""
+    l = w_self._node.length()
+    
+    if l == 0:
+        return space.w_False
+    cased = False
+    iter = rope.CharIterator(w_self._node)
+    for idx in range(l):
+        c = iter.next()
+        if c.isupper():
+            return space.w_False
+        elif not cased and c.islower():
+            cased = True
+    return space.newbool(cased)
+
+def str_istitle__Rope(space, w_self):
+    """Return True if S is a titlecased string and there is at least one
+character in S, i.e. uppercase characters may only follow uncased
+characters and lowercase characters only cased ones. Return False
+otherwise."""
+    cased = False
+    previous_is_cased = False
+
+    iter = rope.CharIterator(w_self._node)
+    for pos in range(0, w_self._node.length()):
+        ch = iter.next()
+        if ch.isupper():
+            if previous_is_cased:
+                return space.w_False
+            previous_is_cased = True
+            cased = True
+        elif ch.islower():
+            if not previous_is_cased:
+                return space.w_False
+            cased = True
+        else:
+            previous_is_cased = False
+
+    return space.newbool(cased)
+
+def str_upper__Rope(space, w_self):
+    l = w_self._node.length()
+    res = [' '] * l
+    iter = rope.CharIterator(w_self._node)
+    for i in range(l):
+        ch = iter.next()
+        res[i] = _upper(ch)
+
+    return W_RopeObject(rope.rope_from_charlist(res))
+
+def str_lower__Rope(space, w_self):
+    l = w_self._node.length()
+    res = [' '] * l
+    iter = rope.CharIterator(w_self._node)
+    for i in range(l):
+        ch = iter.next()
+        res[i] = _lower(ch)
+
+    return W_RopeObject(rope.rope_from_charlist(res))
+
+def str_swapcase__Rope(space, w_self):
+    l = w_self._node.length()
+    res = [' '] * l
+    iter = rope.CharIterator(w_self._node)
+    for i in range(l):
+        ch = iter.next()
+        if ch.isupper():
+            o = ord(ch) + 32
+            res[i] = chr(o)
+        elif ch.islower():
+            o = ord(ch) - 32
+            res[i] = chr(o)
+        else:
+            res[i] = ch
+
+    return W_RopeObject(rope.rope_from_charlist(res))
+
+    
+def str_capitalize__Rope(space, w_self):
+    node = w_self._node
+    length = node.length()
+    buffer = [' '] * length
+    if length > 0:
+        iter = rope.CharIterator(node)
+        ch = iter.next()
+        if ch.islower():
+            o = ord(ch) - 32
+            buffer[0] = chr(o)
+        else:
+            buffer[0] = ch
+
+        for i in range(1, length):
+            ch = iter.next()
+            if ch.isupper():
+                o = ord(ch) + 32
+                buffer[i] = chr(o)
+            else:
+                buffer[i] = ch
+    else:
+        return W_RopeObject.empty
+
+    return W_RopeObject(rope.rope_from_charlist(buffer))
+         
+def str_title__Rope(space, w_self):
+    node = w_self._node
+    length = node.length()
+    buffer = [' '] * length
+    prev_letter = ' '
+
+    iter = rope.CharIterator(node)
+    for pos in range(0, length):
+        ch = iter.next()
+        if not prev_letter.isalpha():
+            buffer[pos] = _upper(ch)
+        else:
+            buffer[pos] = _lower(ch)
+
+        prev_letter = buffer[pos]
+
+    return W_RopeObject(rope.rope_from_charlist(buffer))
+
+def str_split__Rope_None_ANY(space, w_self, w_none, w_maxsplit=-1):
+    maxsplit = space.int_w(w_maxsplit)
+    res_w = []
+    node = w_self._node
+    length = node.length()
+    i = 0
+    iter = rope.CharIterator(node)
+    while True:
+        # find the beginning of the next word
+        while i < length:
+            if not iter.next().isspace():
+                break   # found
+            i += 1
+        else:
+            break  # end of string, finished
+
+        # find the end of the word
+        if maxsplit == 0:
+            j = length   # take all the rest of the string
+        else:
+            j = i + 1
+            while j < length and not iter.next().isspace():
+                j += 1
+            maxsplit -= 1   # NB. if it's already < 0, it stays < 0
+
+        # the word is value[i:j]
+        res_w.append(W_RopeObject(rope.getslice_one(node, i, j)))
+
+        # continue to look from the character following the space after the word
+        i = j + 1
+
+    return space.newlist(res_w)
+
+
+def str_split__Rope_Rope_ANY(space, w_self, w_by, w_maxsplit=-1):
+    maxsplit = space.int_w(w_maxsplit)
+    res_w = []
+    start = 0
+    selfnode = w_self._node
+    bynode = w_by._node
+    iter = rope.FindIterator(selfnode, bynode)
+    bylen = bynode.length()
+    if bylen == 0:
+        raise OperationError(space.w_ValueError, space.wrap("empty separator"))
+
+    while maxsplit != 0:
+        try:
+            next = iter.next()
+        except StopIteration:
+            break
+        res_w.append(W_RopeObject(rope.getslice_one(selfnode, start, next)))
+        start = next + bylen
+        maxsplit -= 1   # NB. if it's already < 0, it stays < 0
+
+    res_w.append(W_RopeObject(rope.getslice_one(
+        selfnode, start, selfnode.length())))
+    return space.newlist(res_w)
+
+def str_rsplit__Rope_None_ANY(space, w_self, w_none, w_maxsplit=-1):
+    # XXX works but flattens
+    maxsplit = space.int_w(w_maxsplit)
+    res_w = []
+    value = w_self._node.flatten()
+    i = len(value)-1
+    while True:
+        # starting from the end, find the end of the next word
+        while i >= 0:
+            if not value[i].isspace():
+                break   # found
+            i -= 1
+        else:
+            break  # end of string, finished
+
+        # find the start of the word
+        # (more precisely, 'j' will be the space character before the word)
+        if maxsplit == 0:
+            j = -1   # take all the rest of the string
+        else:
+            j = i - 1
+            while j >= 0 and not value[j].isspace():
+                j -= 1
+            maxsplit -= 1   # NB. if it's already < 0, it stays < 0
+
+        # the word is value[j+1:i+1]
+        j1 = j + 1
+        assert j1 >= 0
+        res_w.append(wrap_rpystr(value[j1:i+1]))
+
+        # continue to look from the character before the space before the word
+        i = j - 1
+
+    res_w.reverse()
+    return space.newlist(res_w)
+
+def str_rsplit__Rope_Rope_ANY(space, w_self, w_by, w_maxsplit=-1):
+    # XXX works but flattens
+    maxsplit = space.int_w(w_maxsplit)
+    res_w = []
+    value = w_self._node.flatten()
+    end = len(value)
+    by = w_by._node.flatten()
+    bylen = len(by)
+    if bylen == 0:
+        raise OperationError(space.w_ValueError, space.wrap("empty separator"))
+
+    while maxsplit != 0:
+        next = value.rfind(by, 0, end)
+        if next < 0:
+            break
+        res_w.append(wrap_rpystr(value[next+bylen: end]))
+        end = next
+        maxsplit -= 1   # NB. if it's already < 0, it stays < 0
+
+    res_w.append(wrap_rpystr(value[:end]))
+    res_w.reverse()
+    return space.newlist(res_w)
+
+def str_join__Rope_ANY(space, w_self, w_list):
+    list_w = space.unpackiterable(w_list)
+    str_w = space.str_w
+    if list_w:
+        self = w_self._node
+        listlen = 0
+        reslen = 0
+        l = []
+        for i in range(len(list_w)):
+            w_s = list_w[i]
+            if not space.is_true(space.isinstance(w_s, space.w_str)):
+                if space.is_true(space.isinstance(w_s, space.w_unicode)):
+                    w_u = space.call_function(space.w_unicode, w_self)
+                    return space.call_method(w_u, "join", space.newlist(list_w))
+                raise OperationError(
+                    space.w_TypeError,
+                    space.wrap("sequence item %d: expected string, %s "
+                               "found" % (i, space.type(w_s).name)))
+            assert isinstance(w_s, W_RopeObject)
+            l.append(w_s._node)
+        return W_RopeObject(rope.join(w_self._node, l))
+    else:
+        return W_RopeObject.empty
+
+def str_rjust__Rope_ANY_ANY(space, w_self, w_arg, w_fillchar):
+    u_arg = space.int_w(w_arg)
+    selfnode = w_self._node
+    fillchar = space.str_w(w_fillchar)
+    if len(fillchar) != 1:
+        raise OperationError(space.w_TypeError,
+            space.wrap("rjust() argument 2 must be a single character"))
+    
+    d = u_arg - selfnode.length()
+    if d > 0:
+        fillchar = fillchar[0]    # annotator hint: it's a single character
+        resultnode = rope.concatenate(rope.LiteralStringNode(d * fillchar),
+                                      selfnode)
+        return W_RopeObject(resultnode)
+    else:
+        return W_RopeObject(selfnode)
+        
+def str_ljust__Rope_ANY_ANY(space, w_self, w_arg, w_fillchar):
+    u_arg = space.int_w(w_arg)
+    selfnode = w_self._node
+    fillchar = space.str_w(w_fillchar)
+    if len(fillchar) != 1:
+        raise OperationError(space.w_TypeError,
+            space.wrap("rjust() argument 2 must be a single character"))
+    
+    d = u_arg - selfnode.length()
+    if d > 0:
+        fillchar = fillchar[0]    # annotator hint: it's a single character
+        resultnode = rope.concatenate(selfnode,
+                                     rope.LiteralStringNode(d * fillchar))
+        return W_RopeObject(resultnode)
+    else:
+        return W_RopeObject(selfnode)
+
+
+def _convert_idx_params(space, w_self, w_sub, w_start, w_end):
+    self = w_self._node
+    sub = w_sub._node
+    w_len = space.wrap(self.length())
+    w_start = slicetype.adapt_bound(space, w_start, w_len)
+    w_end = slicetype.adapt_bound(space, w_end, w_len)
+
+    start = space.int_w(w_start)
+    end = space.int_w(w_end)
+    assert start >= 0
+    assert end >= 0
+
+    return (self, sub, start, end)
+
+def contains__Rope_Rope(space, w_self, w_sub):
+    self = w_self._node
+    sub = w_sub._node
+    return space.newbool(rope.find(self, sub) >= 0)
+
+def str_find__Rope_Rope_ANY_ANY(space, w_self, w_sub, w_start, w_end):
+
+    (self, sub, start, end) =  _convert_idx_params(space, w_self, w_sub, w_start, w_end)
+    res = rope.find(self, sub, start, end)
+    return wrapint(space, res)
+
+def str_rfind__Rope_Rope_ANY_ANY(space, w_self, w_sub, w_start, w_end):
+    # XXX works but flattens
+    (self, sub, start, end) =  _convert_idx_params(space, w_self, w_sub, w_start, w_end)
+    self = self.flatten()
+    sub = sub.flatten()
+    res = self.rfind(sub, start, end)
+    return wrapint(space, res)
+
+def str_index__Rope_Rope_ANY_ANY(space, w_self, w_sub, w_start, w_end):
+
+    (self, sub, start, end) =  _convert_idx_params(space, w_self, w_sub, w_start, w_end)
+    res = rope.find(self, sub, start, end)
+    if res < 0:
+        raise OperationError(space.w_ValueError,
+                             space.wrap("substring not found in string.index"))
+
+    return wrapint(space, res)
+
+
+def str_rindex__Rope_Rope_ANY_ANY(space, w_self, w_sub, w_start, w_end):
+    (self, sub, start, end) =  _convert_idx_params(space, w_self, w_sub, w_start, w_end)
+    # XXX works but flattens
+    self = self.flatten()
+    sub = sub.flatten()
+    res = self.rfind(sub, start, end)
+    if res < 0:
+        raise OperationError(space.w_ValueError,
+                             space.wrap("substring not found in string.rindex"))
+
+    return wrapint(space, res)
+
+
+def str_replace__Rope_Rope_Rope_ANY(space, w_self, w_sub, w_by, w_maxsplit=-1):
+
+    node = w_self._node
+    length = node.length()
+    sub = w_sub._node
+    by = w_by._node
+    maxsplit = space.int_w(w_maxsplit)
+    if maxsplit == 0:
+        return w_self.create_if_subclassed()
+
+    if not sub.length():
+        upper = node.length()
+        if maxsplit > 0 and maxsplit < upper + 2:
+            upper = maxsplit - 1
+            assert upper >= 0
+        substrings = [by]
+        iter = rope.CharIterator(node)
+        for i in range(upper):
+            substrings.append(rope.LiteralStringNode(iter.next()))
+            substrings.append(by)
+        substrings.append(rope.getslice_one(node, upper, length))
+        return W_RopeObject(rope.rebalance(substrings))
+    startidx = 0
+    substrings = []
+    iter = rope.FindIterator(node, sub)
+    try:
+        foundidx = iter.next()
+    except StopIteration:
+        return w_self.create_if_subclassed()
+    while maxsplit != 0:
+        substrings.append(rope.getslice_one(node, startidx, foundidx))
+        startidx = foundidx + sub.length()
+        try:
+            foundidx = iter.next()
+        except StopIteration:
+            break
+        maxsplit = maxsplit - 1
+    substrings.append(rope.getslice_one(node, startidx, length))
+    return W_RopeObject(rope.join(by, substrings))
+
+def _strip(space, w_self, w_chars, left, right):
+    "internal function called by str_xstrip methods"
+    node = w_self._node
+    length = node.length()
+    u_chars = space.str_w(w_chars)
+    
+    lpos = 0
+    rpos = length
+    
+    if left:
+        #print "while %d < %d and -%s- in -%s-:"%(lpos, rpos, u_self[lpos],w_chars)
+        iter = rope.CharIterator(node)
+        while lpos < rpos and iter.next() in u_chars:
+           lpos += 1
+       
+    if right:
+        # XXX improve this
+        while rpos > lpos and node.getitem(rpos - 1) in u_chars:
+           rpos -= 1
+       
+    return W_RopeObject(rope.getslice_one(node, lpos, rpos))
+
+def _strip_none(space, w_self, left, right):
+    "internal function called by str_xstrip methods"
+    node = w_self._node
+    length = node.length()
+    
+    lpos = 0
+    rpos = length
+    
+    if left:
+        #print "while %d < %d and -%s- in -%s-:"%(lpos, rpos, u_self[lpos],w_chars)
+        iter = rope.CharIterator(node)
+        while lpos < rpos and iter.next().isspace():
+           lpos += 1
+       
+    if right:
+        # XXX fix this
+        while rpos > lpos and node.getitem(rpos - 1).isspace():
+           rpos -= 1
+       
+    assert rpos >= lpos    # annotator hint, don't remove
+    return W_RopeObject(rope.getslice_one(node, lpos, rpos))
+
+def str_strip__Rope_Rope(space, w_self, w_chars):
+    return _strip(space, w_self, w_chars, left=1, right=1)
+
+def str_strip__Rope_None(space, w_self, w_chars):
+    return _strip_none(space, w_self, left=1, right=1)
+   
+def str_rstrip__Rope_Rope(space, w_self, w_chars):
+    return _strip(space, w_self, w_chars, left=0, right=1)
+
+def str_rstrip__Rope_None(space, w_self, w_chars):
+    return _strip_none(space, w_self, left=0, right=1)
+
+   
+def str_lstrip__Rope_Rope(space, w_self, w_chars):
+    return _strip(space, w_self, w_chars, left=1, right=0)
+
+def str_lstrip__Rope_None(space, w_self, w_chars):
+    return _strip_none(space, w_self, left=1, right=0)
+
+
+
+def str_center__Rope_ANY_ANY(space, w_self, w_arg, w_fillchar):
+    node = w_self._node
+    length = node.length()
+    arg  = space.int_w(w_arg)
+    fillchar = space.str_w(w_fillchar)
+    if len(fillchar) != 1:
+        raise OperationError(space.w_TypeError,
+            space.wrap("center() argument 2 must be a single character"))
+
+    d = arg - length
+    if d>0:
+        offset = d//2
+        fillcharnode = rope.LiteralStringNode(fillchar)
+        pre = rope.multiply(fillcharnode, offset)
+        post = rope.multiply(fillcharnode, (d - offset))
+        centered = rope.rebalance([pre, node, post])
+        return W_RopeObject(centered)
+    else:
+        return w_self.create_if_subclassed()
+
+def str_count__Rope_Rope_ANY_ANY(space, w_self, w_arg, w_start, w_end): 
+    selfnode  = w_self._node
+    length = selfnode.length()
+    argnode   = w_arg._node
+
+    w_start = slicetype.adapt_bound(space, w_start, space.wrap(length))
+    w_end = slicetype.adapt_bound(space, w_end, space.wrap(length))
+    u_start = space.int_w(w_start)
+    u_end = space.int_w(w_end)
+    assert u_start >= 0
+    assert u_end >= 0
+    iter = rope.FindIterator(selfnode, argnode, u_start, u_end)
+    i = 0
+    while 1:
+        try:
+            index = iter.next()
+        except StopIteration:
+            break
+        i += 1
+    return wrapint(space, i)
+
+def str_endswith__Rope_Rope_ANY_ANY(space, w_self, w_suffix, w_start, w_end):
+    (self, suffix, start, end) = _convert_idx_params(space, w_self,
+                                                     w_suffix, w_start, w_end)
+    if suffix.length() == 0:
+        return space.w_True
+    if self.length() == 0:
+        return space.w_False
+    begin = end - suffix.length()
+    if begin < start:
+        return space.w_False
+    iter1 = rope.SeekableCharIterator(self)
+    iter1.seekforward(begin)
+    iter2 = rope.CharIterator(suffix)
+    for i in range(suffix.length()):
+        if iter1.next() != iter2.next():
+            return space.w_False
+    return space.w_True
+    
+def str_startswith__Rope_Rope_ANY_ANY(space, w_self, w_prefix, w_start, w_end):
+    (self, prefix, start, end) = _convert_idx_params(space, w_self,
+                                                     w_prefix, w_start, w_end)
+    if prefix.length() == 0:
+        return space.w_True
+    if self.length() == 0:
+        return space.w_False
+    stop = start + prefix.length()
+    if stop > end:
+        return space.w_False
+    iter1 = rope.SeekableCharIterator(self)
+    iter1.seekforward(start)
+    iter2 = rope.CharIterator(prefix)
+    for i in range(prefix.length()):
+        if iter1.next() != iter2.next():
+            return space.w_False
+    return space.w_True
+    
+    
+def _tabindent(node, tabsize):
+    "calculates distance after the token to the next tabstop"
+    # XXX implement reverse char iterator
+    length = node.length()
+    distance = tabsize
+    if length:
+        distance = 0
+        offset = length
+
+        while 1:
+            # no sophisticated linebreak support now
+            # '\r' just for passing adapted CPython test
+            char = node.getitem(offset - 1)
+            if char == "\n" or char == "\r":
+                break
+            distance += 1
+            offset -= 1
+            if offset == 0:
+                break
+                
+        #the same like distance = len(u_token) - (offset + 1)
+        distance = (tabsize - distance) % tabsize
+        if distance == 0:
+            return tabsize
+
+    return distance    
+    
+    
+def str_expandtabs__Rope_ANY(space, w_self, w_tabsize):   
+    node = w_self._node
+    length = node.length()
+    if length == 0:
+        return W_RopeObject.empty
+    tabsize  = space.int_w(w_tabsize)
+    
+    expanded = []
+    iter = rope.FindIterator(node, rope.LiteralStringNode("\t"))
+    #split = u_self.split("\t")
+    #u_expanded = oldtoken = split.pop(0)
+
+    #for token in split:  
+    #    u_expanded += " " * _tabindent(oldtoken,u_tabsize) + token
+    #    oldtoken = token
+    start = 0
+    try:
+        start = iter.next()
+        last = rope.getslice_one(node, 0, start)
+        start += 1
+    except StopIteration:
+        return w_self.create_if_subclassed()
+    expanded.append(last)
+    while 1:
+        expanded.append(rope.multiply(rope.LiteralStringNode(" "),
+                                      _tabindent(last, tabsize)))
+        try:
+            next = iter.next()
+        except StopIteration:
+            break
+        last = rope.getslice_one(node, start, next)
+        expanded.append(last)
+        start = next + 1
+
+    expanded.append(rope.getslice_one(node, start, length))
+    return W_RopeObject(rope.rebalance(expanded))
+ 
+ 
+def str_splitlines__Rope_ANY(space, w_self, w_keepends):
+    #import pdb; pdb.set_trace()
+    keepends  = bool(space.int_w(w_keepends))  # truth value, but type checked
+    node = w_self._node
+    length = node.length()
+    if length == 0:
+        return space.newlist([])
+
+    strs_w = []
+    iter = rope.CharIterator(node)
+    i = j = 0
+    last = " "
+    char = iter.next()
+    while i < length:
+        # Find a line and append it
+        while char != '\n' and char != '\r':
+            try:
+                i += 1
+                last = char
+                char = iter.next()
+            except StopIteration:
+                break
+        # Skip the line break reading CRLF as one line break
+        eol = i
+        i += 1
+        last = char
+        try:
+            char = iter.next()
+        except StopIteration:
+            pass
+        else:
+            if last == '\r' and char == '\n':
+                i += 1
+                try:
+                    last = char
+                    char = iter.next()
+                except StopIteration:
+                    pass
+        if keepends:
+            eol = i
+        strs_w.append(W_RopeObject(rope.getslice_one(node, j, eol)))
+        j = i
+
+    if j == 0:
+        strs_w.append(w_self.create_if_subclassed())
+    elif j < length:
+        strs_w.append(W_RopeObject(rope.getslice_one(node, j, length)))
+
+    return space.newlist(strs_w)
+
+def str_zfill__Rope_ANY(space, w_self, w_width):
+    node = w_self._node
+    length = node.length()
+    width = space.int_w(w_width)
+
+    if length >= width:
+        return w_self.create_if_subclassed()
+    zero = rope.LiteralStringNode("0")
+    if length == 0:
+        return W_RopeObject(rope.multiply(zero, width))
+
+    middle = width - length
+    firstchar = node.getitem(0)
+    if length > 0 and (firstchar == '+' or firstchar == '-'):
+        return W_RopeObject(rope.rebalance(
+            [rope.LiteralStringNode(firstchar),
+             rope.multiply(zero, middle),
+             rope.getslice_one(node, 1, length)]))
+    else:
+        middle = width - length
+        return W_RopeObject(rope.concatenate(
+            rope.multiply(zero, middle), node))
+
+def str_w__Rope(space, w_str):
+    return w_str._node.flatten()
+
+def hash__Rope(space, w_str):
+    node = w_str._node
+    x = rope.hash_rope(node)
+    return wrapint(space, x)
+
+def lt__Rope_Rope(space, w_str1, w_str2):
+    n1 = w_str1._node
+    n2 = w_str2._node
+    return space.newbool(rope.compare(n1, n2) < 0)
+
+def le__Rope_Rope(space, w_str1, w_str2):
+    n1 = w_str1._node
+    n2 = w_str2._node
+    return space.newbool(rope.compare(n1, n2) <= 0)
+
+def eq__Rope_Rope(space, w_str1, w_str2):
+    n1 = w_str1._node
+    n2 = w_str2._node
+    return space.newbool(rope.eq(n1, n2))
+
+def ne__Rope_Rope(space, w_str1, w_str2):
+    n1 = w_str1._node
+    n2 = w_str2._node
+    return space.newbool(not rope.eq(n1, n2))
+
+def gt__Rope_Rope(space, w_str1, w_str2):
+    n1 = w_str1._node
+    n2 = w_str2._node
+    return space.newbool(rope.compare(n1, n2) > 0)
+
+def ge__Rope_Rope(space, w_str1, w_str2):
+    n1 = w_str1._node
+    n2 = w_str2._node
+    return space.newbool(rope.compare(n1, n2) >= 0)
+
+def getitem__Rope_ANY(space, w_str, w_index):
+    ival = space.int_w(w_index)
+    node = w_str._node
+    slen = node.length()
+    if ival < 0:
+        ival += slen
+    if ival < 0 or ival >= slen:
+        exc = space.call_function(space.w_IndexError,
+                                  space.wrap("string index out of range"))
+        raise OperationError(space.w_IndexError, exc)
+    return wrap_rpystr(node.getitem(ival))
+
+def getitem__Rope_Slice(space, w_str, w_slice):
+    node = w_str._node
+    length = node.length()
+    start, stop, step, sl = w_slice.indices4(space, length)
+    if sl == 0:
+        return W_RopeObject.empty
+    return W_RopeObject(rope.getslice(node, start, stop, step, sl))
+
+def mul_string_times(space, w_str, w_times):
+    try:
+        mul = space.int_w(w_times)
+    except OperationError, e:
+        if e.match(space, space.w_TypeError):
+            raise FailedToImplement
+        raise
+    if mul <= 0:
+        return W_RopeObject.empty
+    node = w_str._node
+    length = node.length()
+    try:
+        buflen = ovfcheck(mul * length)
+    except OverflowError:
+        raise OperationError(
+            space.w_OverflowError, 
+            space.wrap("repeated string is too long: %d %d" % (input_len, mul)))
+    return W_RopeObject(rope.multiply(node, mul))
+
+def mul__Rope_ANY(space, w_str, w_times):
+    return mul_string_times(space, w_str, w_times)
+
+def mul__ANY_Rope(space, w_times, w_str):
+    return mul_string_times(space, w_str, w_times)
+
+def add__Rope_Rope(space, w_left, w_right):
+    right = w_right._node
+    left = w_left._node
+    return W_RopeObject(rope.concatenate(left, right))
+
+def len__Rope(space, w_str):
+    return space.wrap(w_str._node.length())
+
+def str__Rope(space, w_str):
+    if type(w_str) is W_RopeObject:
+        return w_str
+    return W_RopeObject(w_str._node)
+
+def iter__Rope(space, w_list):
+    return W_RopeIterObject(w_list)
+
+def ord__Rope(space, w_str):
+    node = w_str._node
+    if node.length() != 1:
+        raise OperationError(
+            space.w_TypeError,
+            space.wrap("ord() expected a character, but string "
+                       "of length %d found"%(len(w_str._value),)))
+    return space.wrap(ord(node.flatten()[0]))
+
+def getnewargs__Rope(space, w_str):
+    return space.newtuple([W_RopeObject(w_str._node)])
+
+def repr__Rope(space, w_str):
+    node = w_str._node
+    length = node.length()
+
+    i = 0
+    buf = [' '] * (length * 4 + 2) # safely overallocate
+
+    quote = "'"
+    if rope.find_char(node, quote) != -1 and rope.find_char(node, '"') == -1:
+        quote = '"'
+
+    buf[0] = quote
+
+    iter = rope.CharIterator(node)
+    while 1:
+        try:
+            c = iter.next()
+            i += 1
+        except StopIteration:
+            break
+        bs_char = None # character quoted by backspace
+
+        if c == '\\' or c == quote:
+            bs_char = c
+        elif c == '\t': bs_char = 't'
+        elif c == '\r': bs_char = 'r'
+        elif c == '\n': bs_char = 'n'
+        elif not '\x20' <= c < '\x7f':
+            n = ord(c)
+            buf[i] = '\\'
+            i += 1
+            buf[i] = 'x'
+            i += 1
+            buf[i] = "0123456789abcdef"[n>>4]
+            i += 1
+            buf[i] = "0123456789abcdef"[n&0xF]
+        else:
+            buf[i] = c
+
+        if bs_char is not None:
+            buf[i] = '\\'
+            i += 1
+            buf[i] = bs_char
+
+    i += 1
+    buf[i] = quote
+
+    return W_RopeObject(rope.rope_from_charlist(buf[:i+1]))
+
+   
+app = gateway.applevel(r'''
+    def str_translate__Rope_ANY_ANY(s, table, deletechars=''):
+        """charfilter - unicode handling is not implemented
+        
+        Return a copy of the string where all characters occurring 
+        in the optional argument deletechars are removed, and the 
+        remaining characters have been mapped through the given translation table, 
+        which must be a string of length 256"""
+
+        if len(table) != 256:
+            raise ValueError("translation table must be 256 characters long")
+
+        L =  [ table[ord(s[i])] for i in range(len(s)) if s[i] not in deletechars ]
+        return ''.join(L)
+
+    def str_decode__Rope_ANY_ANY(str, encoding=None, errors=None):
+        import codecs
+        if encoding is None and errors is None:
+            return unicode(str)
+        elif errors is None:
+            return codecs.getdecoder(encoding)(str)[0]
+        else:
+            return codecs.getdecoder(encoding)(str, errors)[0]
+
+    def str_encode__Rope_ANY_ANY(str, encoding=None, errors=None):
+        import codecs
+        if encoding is None and errors is None:
+            return unicode(str)
+        elif errors is None:
+            return codecs.getencoder(encoding)(str)[0]
+        else:
+            return codecs.getencoder(encoding)(str, errors)[0]
+''', filename=__file__) 
+
+# this one should do the import of _formatting:
+app2 = gateway.applevel('''
+    import _formatting
+
+    def mod__Rope_ANY(format, values):
+        if isinstance(values, tuple):
+            return _formatting.format(format, values, None)
+        else:
+            # CPython's logic for deciding if  ""%values  is
+            # an error (1 value, 0 %-formatters) or not
+            # (values is of a mapping type)
+            if (hasattr(values, '__getitem__')
+                and not isinstance(values, basestring)):
+                return _formatting.format(format, (values,), values)
+            else:
+                return _formatting.format(format, (values,), None)
+''', filename=__file__)
+
+str_translate__Rope_ANY_ANY = app.interphook('str_translate__Rope_ANY_ANY') 
+str_decode__Rope_ANY_ANY = app.interphook('str_decode__Rope_ANY_ANY') 
+str_encode__Rope_ANY_ANY = app.interphook('str_encode__Rope_ANY_ANY') 
+mod__Rope_ANY = app2.interphook('mod__Rope_ANY') 
+
+# methods of the iterator
+
+def iter__RopeIter(space, w_ropeiter):
+    return w_ropeiter
+
+def next__RopeIter(space, w_ropeiter):
+    if w_ropeiter.node is None:
+        raise OperationError(space.w_StopIteration, space.w_None) 
+    try:
+        char = w_ropeiter.char_iter.next()
+        w_item = wrap_rpystr(char)
+    except StopIteration:
+        w_ropeiter.node = None
+        w_ropeiter.char_iter = None
+        raise OperationError(space.w_StopIteration, space.w_None) 
+    w_ropeiter.index += 1 
+    return w_item
+
+def len__RopeIter(space,  w_ropeiter):
+    if w_ropeiter.node is None:
+        return wrapint(space, 0)
+    index = w_ropeiter.index
+    length = w_ropeiter.node.length()
+    result = length - index
+    if result < 0:
+        return wrapint(space, 0)
+    return wrapint(space, result)
+
+# register all methods
+from pypy.objspace.std import stringtype
+register_all(vars(), stringtype)

Modified: pypy/branch/rope-branch/pypy/objspace/std/stringtype.py
==============================================================================
--- pypy/branch/rope-branch/pypy/objspace/std/stringtype.py	(original)
+++ pypy/branch/rope-branch/pypy/objspace/std/stringtype.py	Fri Mar  2 16:24:27 2007
@@ -4,6 +4,7 @@
 from sys import maxint
 
 def sliced(space, s, start, stop):
+    assert not space.config.objspace.std.withrope
     if space.config.objspace.std.withstrslice:
         from pypy.objspace.std.strsliceobject import W_StringSliceObject
         from pypy.objspace.std.stringobject import W_StringObject
@@ -17,6 +18,7 @@
         return W_StringObject(s[start:stop])
 
 def joined(space, strlist):
+    assert not space.config.objspace.std.withrope
     if space.config.objspace.std.withstrjoin:
         from pypy.objspace.std.strjoinobject import W_StringJoinObject
         return W_StringJoinObject(strlist)
@@ -222,9 +224,15 @@
     if space.is_w(w_stringtype, space.w_str):
         return w_obj  # XXX might be reworked when space.str() typechecks
     value = space.str_w(w_obj)
-    w_obj = space.allocate_instance(W_StringObject, w_stringtype)
-    W_StringObject.__init__(w_obj, value)
-    return w_obj
+    if space.config.objspace.std.withrope:
+        from pypy.objspace.std.ropeobject import rope, W_RopeObject
+        w_obj = space.allocate_instance(W_RopeObject, w_stringtype)
+        W_RopeObject.__init__(w_obj, rope.LiteralStringNode(value))
+        return w_obj
+    else:
+        w_obj = space.allocate_instance(W_StringObject, w_stringtype)
+        W_StringObject.__init__(w_obj, value)
+        return w_obj
 
 # ____________________________________________________________
 

Added: pypy/branch/rope-branch/pypy/objspace/std/test/test_rope.py
==============================================================================
--- (empty file)
+++ pypy/branch/rope-branch/pypy/objspace/std/test/test_rope.py	Fri Mar  2 16:24:27 2007
@@ -0,0 +1,307 @@
+import random
+from pypy.objspace.std.rope import *
+
+def make_random_string(operations=10, slicing=True):
+    seed = random.randrange(10000)
+    print seed
+    random.seed(seed)
+    st = "abc"
+    curr = LiteralStringNode(st)
+    if slicing:
+        choice = [0, 1, 2]
+    else:
+        choice = [0, 1]
+    for i in range(operations):
+        a = (chr(random.randrange(ord('a'), ord('z') + 1)) *
+                random.randrange(500))
+        c = random.choice(choice)
+        if c == 0:
+            curr = curr + LiteralStringNode(a)
+            st = st + a
+        elif c == 1:
+            curr = LiteralStringNode(a) + curr
+            st = a + st
+        else:
+            if len(st) < 10:
+                continue
+            start = random.randrange(len(st) // 3)
+            stop = random.randrange(len(st) // 3 * 2, len(st))
+            curr = curr[start: stop]
+            st = st[start: stop]
+    return curr, st
+
+
+def test_add():
+    s = (LiteralStringNode("a" * 32) + LiteralStringNode("bc" * 32) +
+         LiteralStringNode("d" * 32) + LiteralStringNode("ef" * 32) +
+         LiteralStringNode(""))
+    assert s.depth() == 3
+    assert s.flatten() == "".join([c * 32 for c in "a", "bc", "d", "ef"])
+    s = s.rebalance()
+    assert s.flatten() == "".join([c * 32 for c in "a", "bc", "d", "ef"])
+
+def test_dont_rebalance_again():
+    s = (LiteralStringNode("a" * 32) + LiteralStringNode("b" * 32) +
+         LiteralStringNode("d" * 32) + LiteralStringNode("e" * 32) +
+         LiteralStringNode(""))
+    assert s.depth() == 3
+    assert s.flatten() == "".join([c * 32 for c in "abde"])
+    s = s.rebalance()
+    assert s.check_balanced()
+    assert s.balanced
+    assert s.flatten() == "".join([c * 32 for c in "abde"])
+
+def test_random_addition_test():
+    seed = random.randrange(10000)
+    print seed # 4443
+    st = "abc"
+    curr = LiteralStringNode(st)
+    for i in range(1000):
+        a = (chr(random.randrange(ord('a'), ord('z') + 1)) *
+                random.randrange(100))
+        if random.choice([0, 1]):
+            curr = curr + LiteralStringNode(a)
+            st = st + a
+        else:
+            curr = LiteralStringNode(a) + curr
+            st = a + st
+        assert curr.flatten() == st
+    curr = curr.rebalance()
+    assert curr.flatten() == st
+
+def test_getitem():
+    result = "".join([c * 32 for c in "a", "bc", "d", "ef"])
+    s1 = (LiteralStringNode("a" * 32) + LiteralStringNode("bc" * 32) +
+          LiteralStringNode("d" * 32) + LiteralStringNode("ef" * 32) +
+          LiteralStringNode(""))
+    s2 = s1.rebalance()
+    for i in range(len(result)):
+        for s in [s1, s2]:
+            assert s[i] == result[i]
+
+def test_getslice():
+    result = "".join([c * 32 for c in "a", "bc", "d", "ef"])
+    s1 = (LiteralStringNode("a" * 32) + LiteralStringNode("bc" * 32) +
+          LiteralStringNode("d" * 32) + LiteralStringNode("ef" * 32) +
+          LiteralStringNode(""))
+    s2 = s1.rebalance()
+    for s in [s1, s2]:
+        for start in range(0, len(result)):
+            for stop in range(start, len(result)):
+                assert s[start:stop].flatten() == result[start:stop]
+
+def test_getslice_step():
+    s1 = (LiteralStringNode("abcde") + LiteralStringNode("fghijklm") +
+          LiteralStringNode("nopqrstu") + LiteralStringNode("vwxyz") + 
+          LiteralStringNode("zyxwvut") + LiteralStringNode("srqpomnlk"))
+    s2 = s1.rebalance()
+    result = s1.flatten()
+    assert s2.flatten() == result
+    for s in [s1, s2]:
+        for start in range(0, len(result)):
+            for stop in range(start, len(result)):
+                for step in range(1, stop - start):
+                    assert s[start:stop:step].flatten() == result[start:stop:step]
+
+
+def test_random_addition_and_slicing():
+    seed = random.randrange(10000)
+    print seed
+    random.seed(seed)
+    st = "abc"
+    curr = LiteralStringNode(st)
+    last = None
+    all = []
+    for i in range(1000):
+        a = (chr(random.randrange(ord('a'), ord('z') + 1)) *
+                random.randrange(500))
+        last = curr
+        all.append(curr)
+        c = random.choice([0, 1, 2])
+        if c == 0:
+            curr = curr + LiteralStringNode(a)
+            st = st + a
+        elif c == 1:
+            curr = LiteralStringNode(a) + curr
+            st = a + st
+        else:
+            if len(st) < 10:
+                continue
+            # get a significant portion of the string
+            #import pdb; pdb.set_trace()
+            start = random.randrange(len(st) // 3)
+            stop = random.randrange(len(st) // 3 * 2, len(st))
+            curr = curr[start: stop]
+            st = st[start: stop]
+        assert curr.flatten() == st
+    curr = curr.rebalance()
+    assert curr.flatten() == st
+
+def test_iteration():
+    rope, real_st = make_random_string(200)
+    iter = CharIterator(rope)
+    for c in real_st:
+        c2 = iter.next()
+        assert c2 == c
+    py.test.raises(StopIteration, iter.next)
+
+def test_multiply():
+    strs = [(LiteralStringNode("a"), "a"), (LiteralStringNode("abc"), "abc"),
+            make_random_string(500)]
+    times = range(100)
+    for i in range(9, 30):
+        times.append(i ** 2 - 1)
+        times.append(i ** 2)
+        times.append(i ** 2 + 1)
+        times.append(i ** 2 + 2)
+    for r, st in strs:
+        for i in times:
+            r2 = multiply(r, i)
+            assert r2.flatten() == st * i
+
+def test_join():
+    seps = [(LiteralStringNode("a"), "a"), (LiteralStringNode("abc"), "abc"),
+            (LiteralStringNode("d"), "d"), (LiteralStringNode(""), "")]
+    l, strs = zip(*[(LiteralStringNode("x"), "x"),
+                    (LiteralStringNode("xyz"), "xyz"),
+                    (LiteralStringNode("w"), "w")])
+    l = list(l)
+    for s, st in seps:
+        node = join(s, l)
+        result1 = node.flatten()
+        result2 = st.join(strs)
+        for i in range(node.length()):
+            assert result1[i] == result2[i]
+
+def test_join_random():
+    l, strs = zip(*[make_random_string(10 * i) for i in range(1, 5)])
+    l = list(l)
+    seps = [(LiteralStringNode("a"), "a"), (LiteralStringNode("abc"), "abc"),
+            make_random_string(500)]
+    for s, st in seps:
+        node = join(s, l)
+        result1 = node.flatten()
+        result2 = st.join(strs)
+        for i in range(node.length()):
+            assert result1[i] == result2[i]
+
+def test_seekbackward():
+    rope = BinaryConcatNode(BinaryConcatNode(LiteralStringNode("abc"),
+                                             LiteralStringNode("def")),
+                            LiteralStringNode("ghi"))
+    iter = SeekableCharIterator(rope)
+    for c in "abcdefgh":
+        c2 = iter.next()
+        assert c2 == c
+    for i in range(7):
+        iter.seekback(i)
+        for c in "abcdefghi"[-1-i:-1]:
+            c2 = iter.next()
+            assert c2 == c
+    c2 = iter.next()
+    assert c2 == "i"
+    py.test.raises(StopIteration, iter.next)
+
+def test_seekforward():
+    rope = BinaryConcatNode(BinaryConcatNode(LiteralStringNode("abc"),
+                                             LiteralStringNode("def")),
+                            LiteralStringNode("ghi"))
+    rope = rope + rope
+    result = rope.flatten()
+    for j in range(len(result) - 1):
+        for i in range(len(result) - 1 - j):
+            iter = SeekableCharIterator(rope)
+#            if (j, i) == (3, 1):
+#                import pdb; pdb.set_trace()
+            for c in result[:j]:
+                c2 = iter.next()
+                assert c2 == c
+            iter.seekforward(i)
+            for c in result[i + j:]:
+                c2 = iter.next()
+                assert c2 == c
+        py.test.raises(StopIteration, iter.next)
+
+def test_find_char():
+    rope, st = make_random_string()
+    rope = rope[10:100]
+    st = st[10:100]
+    for i in range(len(st)):
+        print i
+        for j in range(i + 1, len(st)):
+            c = st[i:j][(j - i) // 2]
+            pos = find_char(rope, c, i, j)
+            assert pos == st.find(c, i, j)
+
+def test_find_char_bugs():
+    r = find_char(LiteralStringNode("ascii"), " ", 0, 5)
+    assert r == -1
+    r = find_char(LiteralStringNode("a"), "a")
+    assert r == 0
+
+
+def test_restart_positions():
+    restart = construct_restart_positions_node(
+        BinaryConcatNode(LiteralStringNode("aba"), LiteralStringNode("bcabab")))
+    assert restart == [0, 0, 1, 2, 0, 1, 2, 3, 4]
+    restart = construct_restart_positions("ababcabab")
+    assert restart == [0, 0, 1, 2, 0, 1, 2, 3, 4]
+
+def test_find():
+    node = BinaryConcatNode(LiteralStringNode("aba"),
+                            LiteralStringNode("bcabab"))
+    pos = find(node, LiteralStringNode("abc"), 0, node.length())
+    assert pos == 2
+    node = BinaryConcatNode(LiteralStringNode("btffp"),
+                            LiteralStringNode("bacbb"))
+    pos = find(node, LiteralStringNode("a"), 0, node.length())
+    assert pos == 6
+
+def test_fromcharlist():
+    for i in range(0, 100, 10):
+        chars = ["a"] * 50 + ["b"] * i
+        node = rope_from_charlist(chars)
+        assert node.flatten() == "a" * 50  + "b" * i
+    assert rope_from_charlist([]).flatten() == ""
+
+def test_find_iterator():
+    for searchstring in ["abc", "a", "", "x", "xyz"]:
+        node = join(LiteralStringNode(searchstring),
+                    [LiteralStringNode("cde" * i) for i in range(1, 10)])
+    #   node.view()
+        iter = FindIterator(node, LiteralStringNode(searchstring))
+        s = node.flatten()
+        assert s == searchstring.join(["cde" * i for i in range(1, 10)])
+        start = 0
+        while 1:
+            r2 = s.find(searchstring, start)
+            try:
+                r1 = iter.next()
+            except StopIteration:
+                assert r2 == -1
+                break
+            assert r1 == r2
+            start = r2 + max(len(searchstring), 1)
+
+def test_hash():
+    from pypy.rlib.rarithmetic import _hash_string
+    rope, st = make_random_string()
+    assert hash_rope(rope) == _hash_string(st)
+
+def test_equality():
+    l = [make_random_string() for i in range(3)]
+    l.append((LiteralStringNode(""), ""))
+    for rope1, st1 in l:
+        for rope2, st2 in l:
+            assert eq(rope1, rope2) == (st1 == st2)
+
+def test_compare_random():
+    l = [make_random_string() for i in range(3)]
+    l.append((LiteralStringNode(""), ""))
+    for rope1, st1 in l:
+        for rope2, st2 in l:
+            c = compare(rope1, rope2)
+            if c:
+                c = c // abs(c)
+            assert c == cmp(st1, st2)
+

Added: pypy/branch/rope-branch/pypy/objspace/std/test/test_ropeobject.py
==============================================================================
--- (empty file)
+++ pypy/branch/rope-branch/pypy/objspace/std/test/test_ropeobject.py	Fri Mar  2 16:24:27 2007
@@ -0,0 +1,27 @@
+import py
+
+from pypy.objspace.std.test import test_stringobject, test_unicodeobject
+from pypy.conftest import gettestobjspace
+
+class AppTestRopeObject(test_stringobject.AppTestStringObject):
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withrope": True})
+
+class AppTestRopeUnicode(object):
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withrope": True})
+
+    def test_startswith(self):
+        assert "abc".startswith("a", 0, 2147483647)
+
+class AppTestUnicodeRopeStdOnly(test_unicodeobject.AppTestUnicodeStringStdOnly):
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withrope": True})
+
+class AppTestUnicodeRope(test_unicodeobject.AppTestUnicodeString):
+
+    def setup_class(cls):
+        cls.space = gettestobjspace(**{"objspace.std.withrope": True})

Modified: pypy/branch/rope-branch/pypy/objspace/std/unicodeobject.py
==============================================================================
--- pypy/branch/rope-branch/pypy/objspace/std/unicodeobject.py	(original)
+++ pypy/branch/rope-branch/pypy/objspace/std/unicodeobject.py	Fri Mar  2 16:24:27 2007
@@ -1,6 +1,7 @@
 from pypy.objspace.std.objspace import *
 from pypy.interpreter import gateway
 from pypy.objspace.std.stringobject import W_StringObject
+from pypy.objspace.std.ropeobject import W_RopeObject
 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.objspace.std.sliceobject import W_SliceObject
 from pypy.rlib.rarithmetic import intmask, ovfcheck
@@ -100,11 +101,17 @@
 def add__String_Unicode(space, w_left, w_right):
     return space.add(space.call_function(space.w_unicode, w_left) , w_right)
 
+add__Rope_Unicode = add__String_Unicode
+
 def add__Unicode_String(space, w_left, w_right):
     return space.add(w_left, space.call_function(space.w_unicode, w_right))
 
+add__Unicode_Rope = add__Unicode_String
+
 def contains__String_Unicode(space, w_container, w_item):
     return space.contains(space.call_function(space.w_unicode, w_container), w_item )
+contains__Rope_Unicode = contains__String_Unicode
+
 
 def _find(self, sub, start, end):
     if len(sub) == 0:
@@ -388,6 +395,7 @@
 def unicode_strip__Unicode_String(space, w_self, w_chars):
     return space.call_method(w_self, 'strip',
                              space.call_function(space.w_unicode, w_chars))
+unicode_strip__Unicode_Rope = unicode_strip__Unicode_String
 
 def unicode_lstrip__Unicode_None(space, w_self, w_chars):
     return _strip_none(space, w_self, 1, 0)
@@ -397,6 +405,8 @@
     return space.call_method(w_self, 'lstrip',
                              space.call_function(space.w_unicode, w_chars))
 
+unicode_lstrip__Unicode_Rope = unicode_lstrip__Unicode_String
+
 def unicode_rstrip__Unicode_None(space, w_self, w_chars):
     return _strip_none(space, w_self, 0, 1)
 def unicode_rstrip__Unicode_Unicode(space, w_self, w_chars):
@@ -405,6 +415,8 @@
     return space.call_method(w_self, 'rstrip',
                              space.call_function(space.w_unicode, w_chars))
 
+unicode_rstrip__Unicode_Rope = unicode_rstrip__Unicode_String
+
 def unicode_capitalize__Unicode(space, w_self):
     input = w_self._value
     if len(input) == 0:
@@ -999,41 +1011,49 @@
     import stringtype
     W_UnicodeObject = W_UnicodeObject
     from pypy.objspace.std.stringobject import W_StringObject
+    from pypy.objspace.std.ropeobject import W_RopeObject
     def str_strip__String_Unicode(space, w_self, w_chars):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'strip', w_chars)
+    str_strip__Rope_Unicode = str_strip__String_Unicode
     def str_lstrip__String_Unicode(space, w_self, w_chars):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'lstrip', w_chars)
+    str_lstrip__Rope_Unicode = str_lstrip__String_Unicode
     def str_rstrip__String_Unicode(space, w_self, w_chars):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'rstrip', w_chars)
+    str_rstrip__Rope_Unicode = str_rstrip__String_Unicode
     def str_count__String_Unicode_ANY_ANY(space, w_self, w_substr, w_start, w_end):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'count', w_substr, w_start, w_end)
+    str_count__Rope_Unicode_ANY_ANY = str_count__String_Unicode_ANY_ANY
     def str_find__String_Unicode_ANY_ANY(space, w_self, w_substr, w_start, w_end):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'find', w_substr, w_start, w_end)
+    str_find__Rope_Unicode_ANY_ANY = str_find__String_Unicode_ANY_ANY
     def str_rfind__String_Unicode_ANY_ANY(space, w_self, w_substr, w_start, w_end):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'rfind', w_substr, w_start, w_end)
+    str_rfind__Rope_Unicode_ANY_ANY = str_rfind__String_Unicode_ANY_ANY
     def str_index__String_Unicode_ANY_ANY(space, w_self, w_substr, w_start, w_end):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'index', w_substr, w_start, w_end)
+    str_index__Rope_Unicode_ANY_ANY = str_index__String_Unicode_ANY_ANY
     def str_rindex__String_Unicode_ANY_ANY(space, w_self, w_substr, w_start, w_end):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'rindex', w_substr, w_start, w_end)
-
+    str_rindex__Rope_Unicode_ANY_ANY = str_rindex__String_Unicode_ANY_ANY
     def str_replace__String_Unicode_Unicode_ANY(space, w_self, w_old, w_new, w_maxsplit):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'replace', w_old, w_new, w_maxsplit)
-
+    str_replace__Rope_Unicode_Unicode_ANY = str_replace__String_Unicode_Unicode_ANY
     def str_split__String_Unicode_ANY(space, w_self, w_delim, w_maxsplit):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'split', w_delim, w_maxsplit)
-
+    str_split__Rope_Unicode_ANY = str_split__String_Unicode_ANY
     def str_rsplit__String_Unicode_ANY(space, w_self, w_delim, w_maxsplit):
         return space.call_method(space.call_function(space.w_unicode, w_self),
                                  'rsplit', w_delim, w_maxsplit)
-
+    str_rsplit__Rope_Unicode_ANY = str_rsplit__String_Unicode_ANY
     register_all(vars(), stringtype)


More information about the pypy-svn mailing list