Author: Armin Rigo <[email protected]>
Branch: stringbuilder3-perf
Changeset: r72106:4cc95bf7d1d6
Date: 2014-06-20 16:23 +0200
http://bitbucket.org/pypy/pypy/changeset/4cc95bf7d1d6/
Log: Rewrite yet another time rbuilder.py
diff --git a/rpython/rtyper/lltypesystem/rbuilder.py
b/rpython/rtyper/lltypesystem/rbuilder.py
--- a/rpython/rtyper/lltypesystem/rbuilder.py
+++ b/rpython/rtyper/lltypesystem/rbuilder.py
@@ -2,6 +2,7 @@
from rpython.rlib.objectmodel import enforceargs
from rpython.rlib.rarithmetic import ovfcheck, r_uint, intmask
from rpython.rlib.debug import ll_assert
+from rpython.rlib.unroll import unrolling_iterable
from rpython.rtyper.rptr import PtrRepr
from rpython.rtyper.lltypesystem import lltype, rffi, rstr
from rpython.rtyper.lltypesystem.lltype import staticAdtMethod, nullptr
@@ -34,62 +35,15 @@
# ------------------------------------------------------------
+def dont_inline(func):
+ func._dont_inline_ = True
+ return func
+
def always_inline(func):
func._always_inline_ = True
return func
-def new_grow_funcs(name, mallocfn):
-
- @enforceargs(None, int)
- def stringbuilder_grow(ll_builder, needed):
- try:
- needed = ovfcheck(needed + ll_builder.total_size)
- needed = ovfcheck(needed + 63) & ~63
- total_size = ll_builder.total_size + needed
- except OverflowError:
- raise MemoryError
- #
- new_string = mallocfn(needed)
- #
- PIECE = lltype.typeOf(ll_builder.extra_pieces).TO
- old_piece = lltype.malloc(PIECE)
- old_piece.buf = ll_builder.current_buf
- old_piece.prev_piece = ll_builder.extra_pieces
- ll_assert(bool(old_piece.buf), "no buf??")
- ll_builder.current_buf = new_string
- ll_builder.current_pos = 0
- ll_builder.current_end = needed
- ll_builder.total_size = total_size
- ll_builder.extra_pieces = old_piece
-
- def stringbuilder_append_overflow(ll_builder, ll_str, size):
- # First, the part that still fits in the current piece
- part1 = ll_builder.current_end - ll_builder.current_pos
- start = ll_builder.skip
- ll_builder.copy_string_contents(ll_str, ll_builder.current_buf,
- start, ll_builder.current_pos,
- part1)
- ll_builder.skip += part1
- stringbuilder_grow(ll_builder, size - part1)
-
- def stringbuilder_append_overflow_2(ll_builder, char0):
- # Overflow when writing two chars. There are two cases depending
- # on whether one char still fits or not.
- if ll_builder.current_pos < ll_builder.current_end:
- ll_builder.current_buf.chars[ll_builder.current_pos] = char0
- ll_builder.skip = 1
- stringbuilder_grow(ll_builder, 2)
-
- return (func_with_new_name(stringbuilder_grow, '%s_grow' % name),
- func_with_new_name(stringbuilder_append_overflow,
- '%s_append_overflow' % name),
- func_with_new_name(stringbuilder_append_overflow_2,
- '%s_append_overflow_2' % name))
-
-stringbuilder_grows = new_grow_funcs('stringbuilder', rstr.mallocstr)
-unicodebuilder_grows = new_grow_funcs('unicodebuilder', rstr.mallocunicode)
-
STRINGPIECE = lltype.GcStruct('stringpiece',
('buf', lltype.Ptr(STR)),
('prev_piece', lltype.Ptr(lltype.GcForwardReference())))
@@ -100,12 +54,8 @@
('current_pos', lltype.Signed),
('current_end', lltype.Signed),
('total_size', lltype.Signed),
- ('skip', lltype.Signed),
('extra_pieces', lltype.Ptr(STRINGPIECE)),
adtmeths={
- 'grow': staticAdtMethod(stringbuilder_grows[0]),
- 'append_overflow': staticAdtMethod(stringbuilder_grows[1]),
- 'append_overflow_2': staticAdtMethod(stringbuilder_grows[2]),
'copy_string_contents': staticAdtMethod(rstr.copy_string_contents),
'copy_raw_to_string': staticAdtMethod(rstr.copy_raw_to_string),
'mallocfn': staticAdtMethod(rstr.mallocstr),
@@ -122,18 +72,290 @@
('current_pos', lltype.Signed),
('current_end', lltype.Signed),
('total_size', lltype.Signed),
- ('skip', lltype.Signed),
('extra_pieces', lltype.Ptr(UNICODEPIECE)),
adtmeths={
- 'grow': staticAdtMethod(unicodebuilder_grows[0]),
- 'append_overflow': staticAdtMethod(unicodebuilder_grows[1]),
- 'append_overflow_2': staticAdtMethod(unicodebuilder_grows[2]),
'copy_string_contents': staticAdtMethod(rstr.copy_unicode_contents),
'copy_raw_to_string': staticAdtMethod(rstr.copy_raw_to_unicode),
'mallocfn': staticAdtMethod(rstr.mallocunicode),
}
)
+# ------------------------------------------------------------
+# The generic piece of code to append a string (or a slice of it)
+# to a builder; it is inlined inside various functions below
+
+@always_inline
+def _ll_append(ll_builder, ll_str, start, size):
+ pos = ll_builder.current_pos
+ end = ll_builder.current_end
+ if (end - pos) < size:
+ ll_grow_and_append(ll_builder, ll_str, start, size)
+ else:
+ ll_builder.current_pos = pos + size
+ ll_builder.copy_string_contents(ll_str, ll_builder.current_buf,
+ start, pos, size)
+
+# ------------------------------------------------------------
+# Logic to grow a builder (by adding a new string to it)
+
+@dont_inline
+@enforceargs(None, int)
+def ll_grow_by(ll_builder, needed):
+ try:
+ needed = ovfcheck(needed + ll_builder.total_size)
+ needed = ovfcheck(needed + 63) & ~63
+ total_size = ll_builder.total_size + needed
+ except OverflowError:
+ raise MemoryError
+ #
+ new_string = ll_builder.mallocfn(needed)
+ #
+ PIECE = lltype.typeOf(ll_builder.extra_pieces).TO
+ old_piece = lltype.malloc(PIECE)
+ old_piece.buf = ll_builder.current_buf
+ old_piece.prev_piece = ll_builder.extra_pieces
+ ll_assert(bool(old_piece.buf), "no buf??")
+ ll_builder.current_buf = new_string
+ ll_builder.current_pos = 0
+ ll_builder.current_end = needed
+ ll_builder.total_size = total_size
+ ll_builder.extra_pieces = old_piece
+
+@dont_inline
+def ll_grow_and_append(ll_builder, ll_str, start, size):
+ # First, the part that still fits in the current piece
+ part1 = ll_builder.current_end - ll_builder.current_pos
+ ll_assert(part1 < size, "part1 >= size")
+ ll_builder.copy_string_contents(ll_str, ll_builder.current_buf,
+ start, ll_builder.current_pos,
+ part1)
+ start += part1
+ size -= part1
+ # Allocate the new piece
+ ll_grow_by(ll_builder, size)
+ ll_assert(ll_builder.current_pos == 0, "current_pos must be 0 after
grow()")
+ # Finally, the second part of the string
+ ll_builder.current_pos = size
+ ll_builder.copy_string_contents(ll_str, ll_builder.current_buf,
+ start, 0, size)
+
+# ------------------------------------------------------------
+# builder.append()
+
+@always_inline
+def ll_append(ll_builder, ll_str):
+ if jit.we_are_jitted():
+ ll_jit_append(ll_builder, ll_str)
+ else:
+ # no-jit case: inline the logic of _ll_append() in the caller
+ _ll_append(ll_builder, ll_str, 0, len(ll_str.chars))
+
+@dont_inline
+def ll_jit_append(ll_builder, ll_str):
+ # jit case: first try special cases for known small lengths
+ if ll_jit_try_append_slice(ll_builder, ll_str, 0, len(ll_str.chars)):
+ return
+ # fall-back to do a residual call to ll_append_res0
+ ll_append_res0(ll_builder, ll_str)
+
[email protected]_look_inside
+def ll_append_res0(ll_builder, ll_str):
+ _ll_append(ll_builder, ll_str, 0, len(ll_str.chars))
+
+# ------------------------------------------------------------
+# builder.append_char()
+
+@always_inline
+def ll_append_char(ll_builder, char):
+ jit.conditional_call(ll_builder.current_pos == ll_builder.current_end,
+ ll_grow_by, ll_builder, 1)
+ pos = ll_builder.current_pos
+ ll_builder.current_pos = pos + 1
+ ll_builder.current_buf.chars[pos] = char
+
+# ------------------------------------------------------------
+# builder.append_slice()
+
+@always_inline
+def ll_append_slice(ll_builder, ll_str, start, end):
+ if jit.we_are_jitted():
+ ll_jit_append_slice(ll_builder, ll_str, start, end)
+ else:
+ # no-jit case: inline the logic of _ll_append() in the caller
+ _ll_append(ll_builder, ll_str, start, end - start)
+
+@dont_inline
+def ll_jit_append_slice(ll_builder, ll_str, start, end):
+ # jit case: first try special cases for known small lengths
+ if ll_jit_try_append_slice(ll_builder, ll_str, start, end - start):
+ return
+ # fall-back to do a residual call to ll_append_res_slice
+ ll_append_res_slice(ll_builder, ll_str, start, end)
+
[email protected]_look_inside
+def ll_append_res_slice(ll_builder, ll_str, start, end):
+ _ll_append(ll_builder, ll_str, start, end - start)
+
+# ------------------------------------------------------------
+# Special-casing for the JIT: appending strings (or slices) of
+# a known length up to MAX_N. These functions all contain an
+# inlined copy of _ll_append(), but with a known small N, gcc
+# will compile the copy_string_contents() efficiently.
+
+MAX_N = 10
+
+def make_func_for_size(N):
+ @jit.dont_look_inside
+ def ll_append_0(ll_builder, ll_str):
+ _ll_append(ll_builder, ll_str, 0, N)
+ ll_append_0 = func_with_new_name(ll_append_0, "ll_append_0_%d" % N)
+ #
+ @jit.dont_look_inside
+ def ll_append_start(ll_builder, ll_str, start):
+ _ll_append(ll_builder, ll_str, start, N)
+ ll_append_start = func_with_new_name(ll_append_start,
+ "ll_append_start_%d" % N)
+ return ll_append_0, ll_append_start, N
+
+unroll_func_for_size = unrolling_iterable([make_func_for_size(_n)
+ for _n in range(2, MAX_N + 1)])
+
+def ll_jit_try_append_slice(ll_builder, ll_str, start, size):
+ if jit.isconstant(size):
+ if size == 0:
+ return True
+ if size == 1:
+ ll_append_char(ll_builder, ll_str.chars[start])
+ return True
+ for func0, funcstart, for_size in unroll_func_for_size:
+ if size == for_size:
+ if jit.isconstant(start) and start == 0:
+ func0(ll_builder, ll_str)
+ else:
+ funcstart(ll_builder, ll_str, start)
+ return True
+ return False # use the fall-back path
+
+# ------------------------------------------------------------
+# builder.append_multiple_char()
+
+@always_inline
+def ll_append_multiple_char(ll_builder, char, times):
+ if jit.we_are_jitted():
+ if ll_jit_try_append_multiple_char(ll_builder, char, times):
+ return
+ _ll_append_multiple_char(ll_builder, char, times)
+
[email protected]_look_inside
+def _ll_append_multiple_char(ll_builder, char, times):
+ part1 = ll_builder.current_end - ll_builder.current_pos
+ if times > part1:
+ times -= part1
+ buf = ll_builder.current_buf
+ for i in xrange(ll_builder.current_pos, ll_builder.current_end):
+ buf.chars[i] = char
+ ll_grow_by(ll_builder, times)
+ #
+ buf = ll_builder.current_buf
+ pos = ll_builder.current_pos
+ end = pos + times
+ ll_builder.current_pos = end
+ for i in xrange(pos, end):
+ buf.chars[i] = char
+
+def ll_jit_try_append_multiple_char(ll_builder, char, size):
+ if jit.isconstant(size):
+ if size == 0:
+ return True
+ if size == 1:
+ ll_append_char(ll_builder, char)
+ return True
+ return False # use the fall-back path
+
+# ------------------------------------------------------------
+# builder.append_charpsize()
+
[email protected]_look_inside
+def ll_append_charpsize(ll_builder, charp, size):
+ part1 = ll_builder.current_end - ll_builder.current_pos
+ if size > part1:
+ # First, the part that still fits
+ ll_builder.copy_raw_to_string(charp, ll_builder.current_buf,
+ ll_builder.current_pos, part1)
+ charp = rffi.ptradd(charp, part1)
+ size -= part1
+ ll_grow_by(ll_builder, size)
+ #
+ pos = ll_builder.current_pos
+ ll_builder.current_pos = pos + size
+ ll_builder.copy_raw_to_string(charp, ll_builder.current_buf, pos, size)
+
+# ------------------------------------------------------------
+# builder.getlength()
+
+@always_inline
+def ll_getlength(ll_builder):
+ num_chars_missing_from_last_piece = (
+ ll_builder.current_end - ll_builder.current_pos)
+ return ll_builder.total_size - num_chars_missing_from_last_piece
+
+# ------------------------------------------------------------
+# builder.build()
+
[email protected]_inside_iff(lambda ll_builder: jit.isvirtual(ll_builder))
+def ll_build(ll_builder):
+ # NB. usually the JIT doesn't look inside this function; it does
+ # so only in the simplest example where it could virtualize everything
+ if ll_builder.extra_pieces:
+ ll_fold_pieces(ll_builder)
+ elif ll_builder.current_pos != ll_builder.total_size:
+ ll_shrink_final(ll_builder)
+ return ll_builder.current_buf
+
+def ll_shrink_final(ll_builder):
+ final_size = ll_builder.current_pos
+ ll_assert(final_size <= ll_builder.total_size,
+ "final_size > ll_builder.total_size?")
+ buf = rgc.ll_shrink_array(ll_builder.current_buf, final_size)
+ ll_builder.current_buf = buf
+ ll_builder.current_end = final_size
+ ll_builder.total_size = final_size
+
+def ll_fold_pieces(ll_builder):
+ final_size = BaseStringBuilderRepr.ll_getlength(ll_builder)
+ ll_assert(final_size >= 0, "negative final_size")
+ extra = ll_builder.extra_pieces
+ ll_builder.extra_pieces = lltype.nullptr(lltype.typeOf(extra).TO)
+ #
+ result = ll_builder.mallocfn(final_size)
+ piece = ll_builder.current_buf
+ piece_lgt = ll_builder.current_pos
+ ll_assert(ll_builder.current_end == len(piece.chars),
+ "bogus last piece_lgt")
+ ll_builder.total_size = final_size
+ ll_builder.current_buf = result
+ ll_builder.current_pos = final_size
+ ll_builder.current_end = final_size
+
+ dst = final_size
+ while True:
+ dst -= piece_lgt
+ ll_assert(dst >= 0, "rbuilder build: overflow")
+ ll_builder.copy_string_contents(piece, result, 0, dst, piece_lgt)
+ if not extra:
+ break
+ piece = extra.buf
+ piece_lgt = len(piece.chars)
+ extra = extra.prev_piece
+ ll_assert(dst == 0, "rbuilder build: underflow")
+
+# ------------------------------------------------------------
+# bool(builder)
+
+def ll_bool(ll_builder):
+ return ll_builder != nullptr(lltype.typeOf(ll_builder).TO)
+
+# ------------------------------------------------------------
class BaseStringBuilderRepr(AbstractStringBuilderRepr):
def empty(self):
@@ -145,211 +367,24 @@
# Negative values are mapped to 1280.
init_size = intmask(min(r_uint(init_size), r_uint(1280)))
ll_builder = lltype.malloc(cls.lowleveltype.TO)
- ll_builder.current_buf = cls.mallocfn(init_size)
+ ll_builder.current_buf = ll_builder.mallocfn(init_size)
ll_builder.current_pos = 0
ll_builder.current_end = init_size
ll_builder.total_size = init_size
return ll_builder
- @staticmethod
- @always_inline
- def ll_append(ll_builder, ll_str):
- BaseStringBuilderRepr.ll_append_slice(ll_builder, ll_str,
- 0, len(ll_str.chars))
-
- @staticmethod
- @always_inline
- def ll_append_char(ll_builder, char):
- jit.conditional_call(ll_builder.current_pos == ll_builder.current_end,
- ll_builder.grow, ll_builder, 1)
- pos = ll_builder.current_pos
- ll_builder.current_pos = pos + 1
- ll_builder.current_buf.chars[pos] = char
-
- @staticmethod
- def ll_append_char_2(ll_builder, char0, char1):
- # this is only used by the JIT, when appending a small, known-length
- # string. Unlike two consecutive ll_append_char(), it can do that
- # with only one conditional_call.
- ll_builder.skip = 2
- jit.conditional_call(
- ll_builder.current_end - ll_builder.current_pos < 2,
- ll_builder.append_overflow_2, ll_builder, char0)
- pos = ll_builder.current_pos
- buf = ll_builder.current_buf
- buf.chars[pos] = char0
- pos += ll_builder.skip
- ll_builder.current_pos = pos
- buf.chars[pos - 1] = char1
- # NB. this usually writes into buf.chars[current_pos] and
- # buf.chars[current_pos+1], except if we had an overflow right
- # in the middle of the two chars. In that case, 'skip' is set to
- # 1 and only one char is written: the 'char1' overrides the 'char0'.
-
- @staticmethod
- @always_inline
- def ll_append_slice(ll_builder, ll_str, start, end):
- size = end - start
- if jit.we_are_jitted():
- if BaseStringBuilderRepr._ll_jit_try_append_slice(
- ll_builder, ll_str, start, size):
- return
- ll_builder.skip = start
- jit.conditional_call(
- size > ll_builder.current_end - ll_builder.current_pos,
- ll_builder.append_overflow, ll_builder, ll_str, size)
- start = ll_builder.skip
- size = end - start
- pos = ll_builder.current_pos
- ll_builder.copy_string_contents(ll_str, ll_builder.current_buf,
- start, pos, size)
- ll_builder.current_pos = pos + size
-
- @staticmethod
- def _ll_jit_try_append_slice(ll_builder, ll_str, start, size):
- if jit.isconstant(size):
- if size == 0:
- return True
- if size == 1:
- BaseStringBuilderRepr.ll_append_char(ll_builder,
- ll_str.chars[start])
- return True
- if size == 2:
- BaseStringBuilderRepr.ll_append_char_2(ll_builder,
- ll_str.chars[start],
- ll_str.chars[start + 1])
- return True
- return False # use the fall-back path
-
- @staticmethod
- @always_inline
- def ll_append_multiple_char(ll_builder, char, times):
- if jit.we_are_jitted():
- if BaseStringBuilderRepr._ll_jit_try_append_multiple_char(
- ll_builder, char, times):
- return
- BaseStringBuilderRepr._ll_append_multiple_char(ll_builder, char, times)
-
- @staticmethod
- @jit.dont_look_inside
- def _ll_append_multiple_char(ll_builder, char, times):
- part1 = ll_builder.current_end - ll_builder.current_pos
- if times > part1:
- times -= part1
- buf = ll_builder.current_buf
- for i in xrange(ll_builder.current_pos, ll_builder.current_end):
- buf.chars[i] = char
- ll_builder.grow(ll_builder, times)
- #
- buf = ll_builder.current_buf
- pos = ll_builder.current_pos
- end = pos + times
- ll_builder.current_pos = end
- for i in xrange(pos, end):
- buf.chars[i] = char
-
- @staticmethod
- def _ll_jit_try_append_multiple_char(ll_builder, char, size):
- if jit.isconstant(size):
- if size == 0:
- return True
- if size == 1:
- BaseStringBuilderRepr.ll_append_char(ll_builder, char)
- return True
- if size == 2:
- BaseStringBuilderRepr.ll_append_char_2(ll_builder, char, char)
- return True
- if size == 3:
- BaseStringBuilderRepr.ll_append_char(ll_builder, char)
- BaseStringBuilderRepr.ll_append_char_2(ll_builder, char, char)
- return True
- if size == 4:
- BaseStringBuilderRepr.ll_append_char_2(ll_builder, char, char)
- BaseStringBuilderRepr.ll_append_char_2(ll_builder, char, char)
- return True
- return False # use the fall-back path
-
- @staticmethod
- @jit.dont_look_inside
- def ll_append_charpsize(ll_builder, charp, size):
- part1 = ll_builder.current_end - ll_builder.current_pos
- if size > part1:
- # First, the part that still fits
- ll_builder.copy_raw_to_string(charp, ll_builder.current_buf,
- ll_builder.current_pos, part1)
- charp = rffi.ptradd(charp, part1)
- size -= part1
- ll_builder.grow(ll_builder, size)
- #
- pos = ll_builder.current_pos
- ll_builder.current_pos = pos + size
- ll_builder.copy_raw_to_string(charp, ll_builder.current_buf, pos, size)
-
- @staticmethod
- @always_inline
- def ll_getlength(ll_builder):
- num_chars_missing_from_last_piece = (
- ll_builder.current_end - ll_builder.current_pos)
- return ll_builder.total_size - num_chars_missing_from_last_piece
-
- @staticmethod
- @jit.look_inside_iff(lambda ll_builder: jit.isvirtual(ll_builder))
- def ll_build(ll_builder):
- # NB. usually the JIT doesn't look inside this function; it does
- # so only in the simplest example where it could virtualize everything
- if ll_builder.extra_pieces:
- BaseStringBuilderRepr._ll_fold_pieces(ll_builder)
- elif ll_builder.current_pos != ll_builder.total_size:
- BaseStringBuilderRepr._ll_shrink_final(ll_builder)
- return ll_builder.current_buf
-
- @staticmethod
- def _ll_shrink_final(ll_builder):
- final_size = ll_builder.current_pos
- ll_assert(final_size <= ll_builder.total_size,
- "final_size > ll_builder.total_size?")
- buf = rgc.ll_shrink_array(ll_builder.current_buf, final_size)
- ll_builder.current_buf = buf
- ll_builder.current_end = final_size
- ll_builder.total_size = final_size
-
- @staticmethod
- def _ll_fold_pieces(ll_builder):
- final_size = BaseStringBuilderRepr.ll_getlength(ll_builder)
- ll_assert(final_size >= 0, "negative final_size")
- extra = ll_builder.extra_pieces
- ll_builder.extra_pieces = lltype.nullptr(lltype.typeOf(extra).TO)
- #
- result = ll_builder.mallocfn(final_size)
- piece = ll_builder.current_buf
- piece_lgt = ll_builder.current_pos
- ll_assert(ll_builder.current_end == len(piece.chars),
- "bogus last piece_lgt")
- ll_builder.total_size = final_size
- ll_builder.current_buf = result
- ll_builder.current_pos = final_size
- ll_builder.current_end = final_size
-
- dst = final_size
- while True:
- dst -= piece_lgt
- ll_assert(dst >= 0, "rbuilder build: overflow")
- ll_builder.copy_string_contents(piece, result, 0, dst, piece_lgt)
- if not extra:
- break
- piece = extra.buf
- piece_lgt = len(piece.chars)
- extra = extra.prev_piece
- ll_assert(dst == 0, "rbuilder build: underflow")
-
- @classmethod
- def ll_bool(cls, ll_builder):
- return ll_builder != nullptr(cls.lowleveltype.TO)
+ ll_append = staticmethod(ll_append)
+ ll_append_char = staticmethod(ll_append_char)
+ ll_append_slice = staticmethod(ll_append_slice)
+ ll_append_multiple_char = staticmethod(ll_append_multiple_char)
+ ll_append_charpsize = staticmethod(ll_append_charpsize)
+ ll_getlength = staticmethod(ll_getlength)
+ ll_build = staticmethod(ll_build)
+ ll_bool = staticmethod(ll_bool)
class StringBuilderRepr(BaseStringBuilderRepr):
lowleveltype = lltype.Ptr(STRINGBUILDER)
basetp = STR
- mallocfn = staticmethod(rstr.mallocstr)
string_repr = string_repr
char_repr = char_repr
raw_ptr_repr = PtrRepr(
@@ -359,7 +394,6 @@
class UnicodeBuilderRepr(BaseStringBuilderRepr):
lowleveltype = lltype.Ptr(UNICODEBUILDER)
basetp = UNICODE
- mallocfn = staticmethod(rstr.mallocunicode)
string_repr = unicode_repr
char_repr = unichar_repr
raw_ptr_repr = PtrRepr(
diff --git a/rpython/rtyper/test/test_rbuilder.py
b/rpython/rtyper/test/test_rbuilder.py
--- a/rpython/rtyper/test/test_rbuilder.py
+++ b/rpython/rtyper/test/test_rbuilder.py
@@ -28,9 +28,13 @@
def test_simple(self):
sb = StringBuilderRepr.ll_new(3)
+ assert StringBuilderRepr.ll_getlength(sb) == 0
StringBuilderRepr.ll_append_char(sb, 'x')
+ assert StringBuilderRepr.ll_getlength(sb) == 1
StringBuilderRepr.ll_append(sb, llstr("abc"))
+ assert StringBuilderRepr.ll_getlength(sb) == 4
StringBuilderRepr.ll_append_slice(sb, llstr("foobar"), 2, 5)
+ assert StringBuilderRepr.ll_getlength(sb) == 7
StringBuilderRepr.ll_append_multiple_char(sb, 'y', 3)
assert StringBuilderRepr.ll_getlength(sb) == 10
s = StringBuilderRepr.ll_build(sb)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit