1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
15 # Tree-based representation of a String.
17 # Ropes are a data structure introduced in a 1995 paper from
18 # Hans J. Boehm, Russ Atkinson and Michael Plass.
22 # > Ropes: an Alternative to Strings,
23 # > *Software - Practice and Experience*,
24 # > Vol. 25(12), 1315-1330 (December 1995).
26 # The implementation developed here provides an automatic change
27 # of data structure depending on the length of the leaves.
29 # The length from which a `Rope` is built from a `flat` string
30 # if defined at top-level (see `maxlen`) and can be redefined by clients
31 # depending on their needs (e.g. if you need to bench the speed of
32 # the creation of concat nodes, lower the size of maxlen).
34 # A Rope as defined in the original paper is a Tree made of concatenation
35 # nodes and containing `Flat` (that is Array-based) strings as Leaves.
44 # "My" " Name" " is" " Simon."
47 # Note that the above example is not representative of the actual implementation
48 # of `Ropes`, since short leaves are merged to keep the rope at an acceptable
49 # height (hence, this rope here might actually be a `FlatString` !).
54 # Maxlen is the maximum length of a Leaf node
56 # When concatenating two leaves, if `new_length` > `maxlen`,
57 # A `Concat` node is created instead
59 # Its purpose is to limit the depth of the `Rope` (this
60 # improves performance when accessing/iterating).
61 fun maxlen
: Int do return 64
63 # String using a tree-based representation with leaves as `FlatStrings`
64 private abstract class Rope
68 # Node that represents a concatenation between two `String`
73 redef var chars
is lazy
do return new RopeChars(self)
75 redef var bytes
is lazy
do return new RopeBytes(self)
77 redef var length
is noinit
79 redef var bytelen
is noinit
81 redef fun substrings
do return new RopeSubstrings(self)
83 redef fun empty
do return ""
85 redef var to_cstring
is lazy
do
87 var ns
= new NativeString(len
+ 1)
90 for i
in substrings
do
92 i
.as(FlatString).items
.copy_to
(ns
, ilen
, i
.as(FlatString).first_byte
, off
)
98 # Left child of the node
100 # Right child of the node
104 length
= left
.length
+ right
.length
105 bytelen
= left
.bytelen
+ right
.bytelen
113 redef fun iterator
do return new RopeCharIterator(self)
117 for j
in [1 .. i
[ do x
+= self
122 var llen
= left
.length
123 if i
>= llen
then return right
[i
- llen
]
127 redef fun substring
(from
, len
) do
128 var llen
= left
.length
130 if from
+ len
< llen
then return left
.substring
(from
,len
)
131 var lsublen
= llen
- from
132 return left
.substring_from
(from
) + right
.substring
(0, len
- lsublen
)
134 return right
.substring
(from
- llen
, len
)
138 redef fun reversed
do return new Concat(right
.reversed
, left
.reversed
)
140 redef fun insert_at
(s
, pos
) do
141 if pos
> left
.length
then
142 return left
+ right
.insert_at
(s
, pos
- left
.length
)
144 return left
.insert_at
(s
, pos
) + right
147 redef fun to_upper
do return new Concat(left
.to_upper
, right
.to_upper
)
149 redef fun to_lower
do return new Concat(left
.to_lower
, right
.to_lower
)
155 return new Concat(self, s
)
159 if rlen
+ slen
> maxlen
then return new Concat(self, s
)
160 return new Concat(left
, r
+ s
)
164 redef fun copy_to_native
(dest
, n
, src_offset
, dest_offset
) do
165 var subs
= new RopeSubstrings.from
(self, src_offset
)
166 var st
= src_offset
- subs
.pos
167 var off
= dest_offset
170 if n
> it
.length
then
171 var cplen
= it
.length
- st
172 it
.items
.copy_to
(dest
, cplen
, st
, off
)
176 it
.items
.copy_to
(dest
, n
, st
, off
)
185 # Mutable `Rope`, optimized for concatenation operations
187 # A `RopeBuffer` is an efficient way of building a `String` when
188 # concatenating small strings.
190 # It does concatenations in an optimized way by having a
191 # mutable part and an immutable part built by efficiently
192 # concatenating strings in chain.
194 # Every concatenation operation is done by copying a string to
195 # the mutable part and flushing it when full.
197 # However, when a long string is appended to the `Buffer`,
198 # the concatenation is done at it would be in a `Rope`.
203 redef var chars
: Sequence[Char] is lazy
do return new RopeBufferChars(self)
205 redef var bytes
: Sequence[Byte] is lazy
do return new RopeBufferBytes(self)
207 # The final string being built on the fly
208 private var str
: String = ""
210 # Current concatenation buffer
211 private var ns
: NativeString is noinit
213 # Next available (e.g. unset) character in the `Buffer`
216 # Keeps track of the buffer's currently dumped part
218 # This might happen if for instance, a String was being
219 # built by concatenating small parts of string and suddenly
220 # a long string (length > maxlen) is appended.
221 private var dumped
: Int is noinit
223 # Length of the complete rope in chars (0)
234 # Length of the complete rope in bytes
235 redef var bytelen
= 0
237 # Length of the mutable part (in bytes)
239 # Is also used as base to compute the size of the next
240 # mutable native string (`ns`)
241 private var buf_size
: Int is noinit
243 redef fun substrings
do return new RopeBufSubstringIterator(self)
245 # Builds an empty `RopeBuffer`
247 ns
= new NativeString(maxlen
)
252 # Builds a new `RopeBuffer` with `str` in it.
253 init from
(str
: String) do
255 ns
= new NativeString(maxlen
)
261 # Resets the informations of the `Buffer`
263 # This is called when doing in-place modifications
264 # on a previously to_s'd `RopeBuffer`
266 var nns
= new NativeString(buf_size
)
267 var blen
= rpos
- dumped
268 ns
.copy_to
(nns
, blen
, dumped
, 0)
276 if i
< str
.length
then
279 var index
= ns
.char_to_byte_index_cached
(i
- str
.length
, 0, dumped
)
280 return ns
.char_at
(index
)
284 redef fun []=(i
, c
) do
285 assert i
>= 0 and i
<= length
286 if i
== length
then add c
287 if i
< str
.length
then
288 bytelen
+= c
.u8char_len
- str
[i
].u8char_len
290 var l
= s
.substring
(0, i
)
291 var r
= s
.substring_from
(i
+ 1)
294 var reali
= i
- str
.length
295 var index
= ns
.char_to_byte_index_cached
(reali
, 0, dumped
)
296 var st_nxt
= ns
.char_to_byte_index_cached
(reali
+ 1, reali
, index
)
297 var loc_c
= ns
.char_at
(index
)
298 if loc_c
.u8char_len
!= c
.u8char_len
then
299 var delta
= c
.u8char_len
- loc_c
.u8char_len
300 var remsp
= buf_size
- rpos
301 if remsp
< delta
then
303 var nns
= new NativeString(buf_size
)
304 ns
.copy_to
(nns
, index
- dumped
, dumped
, 0)
305 ns
.copy_to
(nns
, rpos
- index
- loc_c
.u8char_len
, index
+ loc_c
.u8char_len
, index
- dumped
+ delta
)
307 index
= index
- dumped
309 ns
.copy_to
(ns
, rpos
- st_nxt
, st_nxt
, st_nxt
+ delta
)
314 ns
.set_char_at
(index
, c
)
318 redef fun empty
do return new RopeBuffer
326 ns
= new NativeString(buf_size
)
331 redef fun substring
(from
, count
) do
332 var strlen
= str
.length
336 if count
< 0 then count
= 0
340 if count
> length
then count
= length
- from
342 if count
== 0 then return empty
344 if from
< strlen
then
345 var subpos
= strlen
- from
346 if count
<= subpos
then
347 return new RopeBuffer.from
(str
.substring
(from
, count
))
349 var l
= str
.substring_from
(from
)
350 var rem
= count
- subpos
351 var nns
= new NativeString(rem
)
352 ns
.copy_to
(nns
, rem
, dumped
, 0)
353 return new RopeBuffer.from
(l
+ nns
.to_s_with_length
(rem
))
356 var nns
= new NativeString(count
)
357 ns
.copy_to
(nns
, count
, dumped
, 0)
358 return new RopeBuffer.from
(nns
.to_s_with_length
(count
))
362 redef fun append
(s
) do
364 if slen
>= maxlen
then
369 if s
isa FlatText then
371 var from
= if s
isa FlatString then s
.first_byte
else 0
372 var remsp
= buf_size
- rpos
373 if slen
<= remsp
then
374 oits
.copy_to
(ns
, slen
, from
, rpos
)
378 var brk
= oits
.find_beginning_of_char_at
(from
+ remsp
)
379 oits
.copy_to
(ns
, brk
, from
, rpos
)
382 oits
.copy_to
(ns
, slen
- remsp
, brk
, 0)
385 for i
in s
.substrings
do append i
391 if rp
>= buf_size
then
395 # TODO: Fix when supporting UTF-8
396 ns
[rp
] = c
.ascii
.to_b
402 private fun add_byte
(b
: Byte) do
404 if rp
>= buf_size
then
414 # Converts the Buffer to a FlatString, appends it to
415 # the final String and re-allocates a new larger Buffer.
416 private fun dump_buffer
do
418 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
422 ns
= new NativeString(bs
)
428 # Similar to dump_buffer, but does not reallocate a new NativeString
429 private fun persist_buffer
do
430 if rpos
== dumped
then return
431 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
438 new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1).output
441 # Enlarge is useless here since the `Buffer`
442 # part is automatically dumped when necessary.
444 # Also, since the buffer can not be overused by a
445 # single string, there is no need for manual
448 # "You have no power here !"
449 redef fun enlarge
(i
) do end
457 # Flush the buffer in order to only have to reverse `str`.
458 if rpos
> 0 and dumped
!= rpos
then
459 str
+= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
466 if written
then reset
472 if written
then reset
478 redef class FlatString
480 redef fun insert_at
(s
, pos
) do
481 var l
= substring
(0, pos
)
482 var r
= substring_from
(pos
)
490 if slen
== 0 then return self
491 if mlen
== 0 then return s
492 var nlen
= slen
+ mlen
493 if s
isa FlatString then
494 if nlen
> maxlen
then return new Concat(self, s
)
496 var sifrom
= s
.first_byte
497 var mifrom
= first_byte
499 var ns
= new NativeString(nlen
+ 1)
500 mits
.copy_to
(ns
, mlen
, mifrom
, 0)
501 sits
.copy_to
(ns
, slen
, sifrom
, mlen
)
502 return ns
.to_s_with_length
(nlen
)
503 else if s
isa Concat then
505 var sllen
= sl
.bytelen
506 if sllen
+ mlen
> maxlen
then return new Concat(self, s
)
507 return new Concat(self + sl
, s
.right
)
514 # A simple linked list for use with iterators
515 private class RopeCharIteratorPiece
516 # The encapsulated node of the `Rope`
518 # Was its left child (if any) visited ?
520 # Was its right child (if any) visited ?
522 # The previous node in the list.
523 var prev
: nullable RopeCharIteratorPiece
526 # A reverse iterator capable of working with `Rope` objects
527 private class RopeByteReverseIterator
528 super IndexedIterator[Byte]
530 # Current NativeString
532 # Current position in NativeString
534 # Position in the Rope (0-indexed)
536 # Iterator on the substrings, does the Postfix part of
537 # the Rope traversal.
538 var subs
: IndexedIterator[FlatString]
540 init(root
: Concat) is old_style_init
do
541 pos
= root
.bytelen
- 1
542 subs
= new ReverseRopeSubstrings(root
)
548 init from
(root
: Concat, pos
: Int) do
550 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
553 pns
= pos
- subs
.index
556 redef fun index
do return pos
558 redef fun is_ok
do return pos
>= 0
560 redef fun item
do return ns
[pns
]
565 if pns
>= 0 then return
566 if not subs
.is_ok
then return
568 if not subs
.is_ok
then return
575 # Forward iterator on the bytes of a `Rope`
576 private class RopeByteIterator
577 super IndexedIterator[Byte]
579 # Position in current `String`
581 # Current `String` being iterated on
583 # Substrings of the Rope
584 var subs
: IndexedIterator[FlatString]
585 # Maximum position to iterate on (e.g. Rope.length)
587 # Position (char) in the Rope (0-indexed)
590 init(root
: Concat) is old_style_init
do
591 subs
= new RopeSubstrings(root
)
594 max
= root
.length
- 1
598 init from
(root
: Concat, pos
: Int) do
599 subs
= new RopeSubstrings.from
(root
, pos
)
600 pns
= pos
- subs
.index
603 max
= root
.length
- 1
606 redef fun item
do return ns
[pns
]
608 redef fun is_ok
do return pos
<= max
610 redef fun index
do return pos
615 if pns
< subs
.item
.bytelen
then return
616 if not subs
.is_ok
then return
618 if not subs
.is_ok
then return
625 # A reverse iterator capable of working with `Rope` objects
626 private class RopeCharReverseIterator
627 super IndexedIterator[Char]
629 # Current NativeString
631 # Current position in NativeString
633 # Position in the Rope (0-indexed)
635 # Iterator on the substrings, does the Postfix part of
636 # the Rope traversal.
637 var subs
: IndexedIterator[String]
639 init(root
: Concat) is old_style_init
do
640 pos
= root
.length
- 1
641 subs
= new ReverseRopeSubstrings(root
)
646 init from
(root
: Concat, pos
: Int) do
648 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
650 pns
= pos
- subs
.index
653 redef fun index
do return pos
655 redef fun is_ok
do return pos
>= 0
657 redef fun item
do return ns
[pns
]
662 if pns
>= 0 then return
663 if not subs
.is_ok
then return
665 if not subs
.is_ok
then return
671 # Forward iterator on the chars of a `Rope`
672 private class RopeCharIterator
673 super IndexedIterator[Char]
675 # Position in current `String`
677 # Current `String` being iterated on
679 # Substrings of the Rope
680 var subs
: IndexedIterator[String]
681 # Maximum position to iterate on (e.g. Rope.length)
683 # Position (char) in the Rope (0-indexed)
686 init(root
: Concat) is old_style_init
do
687 subs
= new RopeSubstrings(root
)
690 max
= root
.length
- 1
694 init from
(root
: Concat, pos
: Int) do
695 subs
= new RopeSubstrings.from
(root
, pos
)
696 pns
= pos
- subs
.index
699 max
= root
.length
- 1
702 redef fun item
do return str
[pns
]
704 redef fun is_ok
do return pos
<= max
706 redef fun index
do return pos
711 if pns
< subs
.item
.length
then return
712 if not subs
.is_ok
then return
714 if not subs
.is_ok
then return
720 # Substrings of a Rope (i.e. Reverse postfix iterator on leaves)
721 private class ReverseRopeSubstrings
722 super IndexedIterator[FlatString]
725 var iter
: RopeCharIteratorPiece is noinit
727 var pos
: Int is noinit
730 var str
: FlatString is noinit
732 init(root
: Concat) is old_style_init
do
733 var r
= new RopeCharIteratorPiece(root
, false, true, null)
734 pos
= root
.length
- 1
735 var lnod
: String = root
737 if lnod
isa Concat then
739 r
= new RopeCharIteratorPiece(lnod
, false, true, r
)
741 str
= lnod
.as(FlatString)
748 init from
(root
: Concat, pos
: Int) do
749 var r
= new RopeCharIteratorPiece(root
, false, true, null)
750 var rnod
: String = root
753 if rnod
isa Concat then
754 if off
>= rnod
.left
.length
then
755 off
-= rnod
.left
.length
757 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
761 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
764 str
= rnod
.as(FlatString)
773 redef fun item
do return str
775 redef fun index
do return pos
777 redef fun is_ok
do return pos
>= 0
780 if pos
< 0 then return
782 var currit
= curr
.node
783 while curr
!= null do
785 if not currit
isa Concat then
786 str
= currit
.as(FlatString)
791 if not curr
.rdone
then
793 curr
= new RopeCharIteratorPiece(currit
.right
, false, false, curr
)
796 if not curr
.ldone
then
798 curr
= new RopeCharIteratorPiece(currit
.left
, false, false, curr
)
807 private class RopeBufSubstringIterator
808 super Iterator[FlatText]
810 # Iterator on the substrings of the building string
811 var iter
: Iterator[FlatText]
812 # Makes a String out of the buffered part of the Ropebuffer
813 var nsstr
: FlatString
814 # Did we attain the buffered part ?
815 var nsstr_done
= false
817 init(str
: RopeBuffer) is old_style_init
do
818 iter
= str
.str
.substrings
819 nsstr
= new FlatString.with_infos
(str
.ns
, str
.rpos
- str
.dumped
, str
.dumped
, str
.rpos
- 1)
820 if str
.length
== 0 then nsstr_done
= true
823 redef fun is_ok
do return iter
.is_ok
or not nsstr_done
827 if iter
.is_ok
then return iter
.item
840 # Substrings of a Rope (i.e. Postfix iterator on leaves)
841 private class RopeSubstrings
842 super IndexedIterator[FlatString]
845 var iter
: RopeCharIteratorPiece is noinit
847 var pos
: Int is noinit
848 # Maximum position in `Rope` (i.e. length - 1)
849 var max
: Int is noinit
852 var str
: FlatString is noinit
854 init(root
: Concat) is old_style_init
do
855 var r
= new RopeCharIteratorPiece(root
, true, false, null)
857 max
= root
.length
- 1
858 var rnod
: String = root
860 if rnod
isa Concat then
862 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
864 str
= rnod
.as(FlatString)
872 init from
(root
: Concat, pos
: Int) do
873 var r
= new RopeCharIteratorPiece(root
, true, false, null)
874 max
= root
.length
- 1
875 var rnod
: String = root
878 if rnod
isa Concat then
879 if off
>= rnod
.left
.length
then
881 off
-= rnod
.left
.length
883 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
886 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
889 str
= rnod
.as(FlatString)
898 redef fun item
do return str
900 redef fun is_ok
do return pos
<= max
902 redef fun index
do return pos
906 if pos
> max
then return
910 if not rnod
isa Concat then
913 str
= rnod
.as(FlatString)
914 iter
= it
.as(not null)
920 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
921 else if not it
.rdone
then
924 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
934 # Implementation of a `StringCharView` for `Concat` objects
935 private class RopeChars
938 redef type SELFTYPE: Concat
944 redef fun iterator_from
(i
) do return new RopeCharIterator.from
(target
, i
)
946 redef fun reverse_iterator_from
(i
) do return new RopeCharReverseIterator.from
(target
, i
)
950 # Implementation of a `StringCharView` for `Concat` objects
951 private class RopeBytes
954 redef type SELFTYPE: Concat
957 var nod
: String = target
959 if nod
isa FlatString then return nod
.items
[i
]
960 if not nod
isa Concat then abort
961 if nod
.left
.bytelen
>= i
then
969 redef fun iterator_from
(i
) do return new RopeByteIterator.from
(target
, i
)
971 redef fun reverse_iterator_from
(i
) do return new RopeByteReverseIterator.from
(target
, i
)
975 # An Iterator over a RopeBuffer.
976 class RopeBufferCharIterator
977 super IndexedIterator[Char]
980 var sit
: IndexedIterator[Char]
982 redef fun index
do return sit
.index
984 # Init the iterator from a RopeBuffer.
985 init(t
: RopeBuffer) is old_style_init
do
987 sit
= t
.str
.chars
.iterator
990 # Init the iterator from a RopeBuffer starting from `pos`.
991 init from
(t
: RopeBuffer, pos
: Int) do
993 sit
= t
.str
.chars
.iterator_from
(pos
)
996 redef fun is_ok
do return sit
.is_ok
1003 redef fun next
do sit
.next
1006 # Reverse iterator over a RopeBuffer.
1007 class RopeBufferCharReverseIterator
1008 super IndexedIterator[Char]
1011 var sit
: IndexedIterator[Char]
1013 redef fun index
do return sit
.index
1015 # Init the iterator from a RopeBuffer.
1016 init(tgt
: RopeBuffer) is old_style_init
do
1018 sit
= tgt
.str
.chars
.reverse_iterator
1021 # Init the iterator from a RopeBuffer starting from `pos`.
1022 init from
(tgt
: RopeBuffer, pos
: Int) do
1024 sit
= tgt
.str
.chars
.reverse_iterator_from
(pos
)
1027 redef fun is_ok
do return sit
.is_ok
1034 redef fun next
do sit
.next
1037 # View on the chars of a `RopeBuffer`
1038 class RopeBufferChars
1039 super BufferCharView
1041 redef type SELFTYPE: RopeBuffer
1043 redef fun [](i
) do return target
[i
]
1045 redef fun []=(i
,c
) do target
[i
] = c
1047 redef fun add
(c
) do target
.add c
1049 redef fun push
(c
) do target
.add c
1051 redef fun iterator_from
(i
) do return new RopeBufferCharIterator.from
(target
, i
)
1053 redef fun reverse_iterator_from
(i
) do return new RopeBufferCharReverseIterator.from
(target
, i
)
1056 # An Iterator over a RopeBuffer.
1057 class RopeBufferByteIterator
1058 super IndexedIterator[Byte]
1061 var sit
: IndexedIterator[Byte]
1063 # Native string iterated over.
1064 var ns
: NativeString
1066 # Current position in `ns`.
1069 # Maximum position iterable.
1074 # Init the iterator from a RopeBuffer.
1075 init(t
: RopeBuffer) is old_style_init
do
1078 sit
= t
.str
.bytes
.iterator
1083 # Init the iterator from a RopeBuffer starting from `pos`.
1084 init from
(t
: RopeBuffer, pos
: Int) do
1087 sit
= t
.str
.bytes
.iterator_from
(pos
)
1088 pns
= pos
- t
.str
.length
1092 redef fun is_ok
do return index
< maxpos
1095 if sit
.is_ok
then return sit
.item
1109 # Reverse iterator over a RopeBuffer.
1110 class RopeBufferByteReverseIterator
1111 super IndexedIterator[Byte]
1114 var sit
: IndexedIterator[Byte]
1116 # Native string iterated over.
1117 var ns
: NativeString
1119 # Current position in `ns`.
1124 # Init the iterator from a RopeBuffer.
1125 init(tgt
: RopeBuffer) is old_style_init
do
1126 sit
= tgt
.str
.bytes
.reverse_iterator
1128 index
= tgt
.bytelen
- 1
1132 # Init the iterator from a RopeBuffer starting from `pos`.
1133 init from
(tgt
: RopeBuffer, pos
: Int) do
1134 sit
= tgt
.str
.bytes
.reverse_iterator_from
(pos
- (tgt
.rpos
- tgt
.dumped
))
1135 pns
= pos
- tgt
.str
.bytelen
+ tgt
.rpos
1140 redef fun is_ok
do return index
>= 0
1143 if pns
>= 0 then return ns
[pns
]
1157 # View on the chars of a `RopeBuffer`
1158 class RopeBufferBytes
1159 super BufferByteView
1161 redef type SELFTYPE: RopeBuffer
1164 if i
< target
.str
.bytelen
then
1165 return target
.str
.bytes
[i
]
1167 return target
.ns
[i
- target
.str
.bytelen
]
1171 redef fun []=(i
,c
) do
1172 if i
== target
.length
then target
.add_byte c
1173 if i
< target
.str
.length
then
1174 # FIXME: Will need to be optimized and rewritten with Unicode
1176 var l
= s
.substring
(0, i
)
1177 var r
= s
.substring_from
(i
+ 1)
1178 target
.str
= l
+ c
.to_i
.ascii
.to_s
+ r
1180 target
.ns
[i
- target
.str
.length
] = c
1184 redef fun add
(c
) do target
.add_byte c
1186 redef fun push
(c
) do target
.add_byte c
1188 redef fun iterator_from
(i
) do return new RopeBufferByteIterator.from
(target
, i
)
1190 redef fun reverse_iterator_from
(i
) do return new RopeBufferByteReverseIterator.from
(target
, i
)