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.from
(self, 0)
83 redef fun empty
do return ""
85 # Cache for the latest accessed FlatString in `self`
86 var flat_cache
: String = ""
88 # Position of the beginning of `flat_cache` in `self`
89 var flat_last_pos_start
: Int = -1
91 redef var to_cstring
is lazy
do
93 var ns
= new NativeString(len
+ 1)
96 for i
in substrings
do
98 i
.as(FlatString)._items
.copy_to
(ns
, ilen
, i
.as(FlatString)._first_byte
, off
)
104 # Left child of the node
106 # Right child of the node
112 length
= l
.length
+ r
.length
113 _bytelen
= l
.bytelen
+ r
.bytelen
121 redef fun iterator
do return new RopeCharIterator.from
(self, 0)
125 for j
in [1 .. i
[ do x
+= self
130 if flat_last_pos_start
!= -1 then
131 var fsp
= i
- flat_last_pos_start
132 if fsp
>= 0 and fsp
< flat_cache
.length
then return flat_cache
[fsp
]
137 if s
isa FlatString then break
140 var llen
= lft
.length
148 flat_last_pos_start
= st
- i
153 redef fun substring
(from
, len
) do
155 var llen
= lft
.length
157 if from
+ count
< llen
then return lft
.substring
(from
, count
)
158 var lsublen
= llen
- from
159 return lft
.substring_from
(from
) + _right
.substring
(0, count
- lsublen
)
161 return _right
.substring
(from
- llen
, count
)
165 redef fun reversed
do return new Concat(_right
.reversed
, _left
.reversed
)
167 redef fun insert_at
(s
, pos
) do
169 if pos
> lft
.length
then
170 return lft
+ _right
.insert_at
(s
, pos
- lft
.length
)
172 return lft
.insert_at
(s
, pos
) + _right
175 redef fun to_upper
do return new Concat(_left
.to_upper
, _right
.to_upper
)
177 redef fun to_lower
do return new Concat(_left
.to_lower
, _right
.to_lower
)
183 return new Concat(self, s
)
187 if rlen
+ slen
> maxlen
then return new Concat(self, s
)
188 return new Concat(_left
, r
+ s
)
192 redef fun copy_to_native
(dest
, n
, src_offset
, dest_offset
) do
193 var subs
= new RopeSubstrings.from
(self, src_offset
)
194 var st
= src_offset
- subs
.pos
195 var off
= dest_offset
198 if n
> it
.length
then
199 var cplen
= it
.length
- st
200 it
._items
.copy_to
(dest
, cplen
, st
, off
)
204 it
._items
.copy_to
(dest
, n
, st
, off
)
212 # Returns a balanced version of `self`
213 fun balance
: String do
214 var children
= new Array[String]
216 var iter
: nullable RopeCharIteratorPiece = new RopeCharIteratorPiece(self, false, false, null)
218 if iter
== null then break
220 if not rnod
isa Concat then
225 if not iter
.ldone
then
227 iter
= new RopeCharIteratorPiece(rnod
._left
, false, false, iter
)
228 else if not iter
.rdone
then
230 iter
= new RopeCharIteratorPiece(rnod
._right
, false, false, iter
)
236 return recurse_balance
(children
, children
.length
)
239 fun recurse_balance
(nodes
: Array[String], len
: Int): String do
243 if len
- stpos
> 1 then
244 nodes
[finpos
] = new Concat(nodes
[stpos
], nodes
[stpos
+ 1])
247 nodes
[finpos
] = nodes
[stpos
]
252 if finpos
== 1 then return nodes
[0]
253 return recurse_balance
(nodes
, finpos
)
257 # Mutable `Rope`, optimized for concatenation operations
259 # A `RopeBuffer` is an efficient way of building a `String` when
260 # concatenating small strings.
262 # It does concatenations in an optimized way by having a
263 # mutable part and an immutable part built by efficiently
264 # concatenating strings in chain.
266 # Every concatenation operation is done by copying a string to
267 # the mutable part and flushing it when full.
269 # However, when a long string is appended to the `Buffer`,
270 # the concatenation is done at it would be in a `Rope`.
275 redef var chars
: Sequence[Char] is lazy
do return new RopeBufferChars(self)
277 redef var bytes
is lazy
do return new RopeBufferBytes(self)
279 # The final string being built on the fly
280 private var str
: String = ""
282 # Current concatenation buffer
283 private var ns
: NativeString is noinit
285 # Next available (e.g. unset) character in the `Buffer`
288 # Length (in chars) of the buffered part
289 private var nslen
= 0
291 # Keeps track of the buffer's currently dumped part
293 # This might happen if for instance, a String was being
294 # built by concatenating small parts of string and suddenly
295 # a long string (length > maxlen) is appended.
296 private var dumped
: Int is noinit
298 # Length of the complete rope in chars (0)
309 # Length of the complete rope in bytes
310 redef var bytelen
= 0
312 # Length of the mutable part (in bytes)
314 # Is also used as base to compute the size of the next
315 # mutable native string (`ns`)
316 private var buf_size
: Int is noinit
318 redef fun substrings
do return new RopeBufSubstringIterator.from
(self)
320 # Builds an empty `RopeBuffer`
322 ns
= new NativeString(maxlen
)
327 # Builds a new `RopeBuffer` with `str` in it.
328 init from
(str
: String) do
330 ns
= new NativeString(maxlen
)
332 _bytelen
= str
.length
336 # Resets the informations of the `Buffer`
338 # This is called when doing in-place modifications
339 # on a previously to_s'd `RopeBuffer`
341 var nns
= new NativeString(buf_size
)
342 var blen
= rpos
- dumped
343 ns
.copy_to
(nns
, blen
, dumped
, 0)
351 if i
< str
.length
then
354 var index
= ns
.char_to_byte_index_cached
(i
- str
.length
, 0, dumped
)
355 return ns
.char_at
(index
)
359 redef fun []=(i
, c
) do
360 assert i
>= 0 and i
<= length
361 if i
== length
then add c
362 if i
< str
.length
then
363 _bytelen
+= c
.u8char_len
- str
[i
].u8char_len
365 var l
= s
.substring
(0, i
)
366 var r
= s
.substring_from
(i
+ 1)
369 var reali
= i
- str
.length
370 var index
= ns
.char_to_byte_index_cached
(reali
, 0, dumped
)
371 var st_nxt
= ns
.char_to_byte_index_cached
(reali
+ 1, reali
, index
)
372 var loc_c
= ns
.char_at
(index
)
373 if loc_c
.u8char_len
!= c
.u8char_len
then
374 var delta
= c
.u8char_len
- loc_c
.u8char_len
375 var remsp
= buf_size
- rpos
376 if remsp
< delta
then
378 var nns
= new NativeString(buf_size
)
379 ns
.copy_to
(nns
, index
- dumped
, dumped
, 0)
380 ns
.copy_to
(nns
, rpos
- index
- loc_c
.u8char_len
, index
+ loc_c
.u8char_len
, index
- dumped
+ delta
)
382 index
= index
- dumped
384 ns
.copy_to
(ns
, rpos
- st_nxt
, st_nxt
, st_nxt
+ delta
)
389 ns
.set_char_at
(index
, c
)
393 redef fun empty
do return new RopeBuffer
401 ns
= new NativeString(buf_size
)
406 redef fun substring
(from
, count
) do
407 var strlen
= str
.length
411 if count
< 0 then count
= 0
415 if count
> length
then count
= length
- from
417 if count
== 0 then return empty
419 if from
< strlen
then
420 var subpos
= strlen
- from
421 if count
<= subpos
then
422 return new RopeBuffer.from
(str
.substring
(from
, count
))
424 var l
= str
.substring_from
(from
)
425 var rem
= count
- subpos
426 var nns
= new NativeString(rem
)
427 ns
.copy_to
(nns
, rem
, dumped
, 0)
428 return new RopeBuffer.from
(l
+ nns
.to_s_unsafe
(rem
))
431 var nns
= new NativeString(count
)
432 ns
.copy_to
(nns
, count
, dumped
, 0)
433 return new RopeBuffer.from
(nns
.to_s_unsafe
(count
))
437 redef fun append
(s
) do
439 if slen
>= maxlen
then
444 if s
isa FlatText then
446 var from
= s
.first_byte
447 var remsp
= buf_size
- rpos
448 if slen
<= remsp
then
449 oits
.copy_to
(ns
, slen
, from
, rpos
)
453 var brk
= oits
.find_beginning_of_char_at
(from
+ remsp
)
454 oits
.copy_to
(ns
, brk
, from
, rpos
)
457 oits
.copy_to
(ns
, slen
- remsp
, brk
, 0)
460 for i
in s
.substrings
do append i
466 var remsp
= buf_size
- rp
467 var cln
= c
.u8char_len
472 ns
.set_char_at
(rp
, c
)
478 # Converts the Buffer to a FlatString, appends it to
479 # the final String and re-allocates a new larger Buffer.
480 private fun dump_buffer
do
482 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
)
486 ns
= new NativeString(bs
)
492 # Similar to dump_buffer, but does not reallocate a new NativeString
493 private fun persist_buffer
do
494 if rpos
== dumped
then return
495 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
)
502 new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
).output
505 # Enlarge is useless here since the `Buffer`
506 # part is automatically dumped when necessary.
508 # Also, since the buffer can not be overused by a
509 # single string, there is no need for manual
512 # "You have no power here !"
513 redef fun enlarge
(i
) do end
522 # Flush the buffer in order to only have to reverse `str`.
523 if rpos
> 0 and dumped
!= rpos
then
524 str
+= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
)
531 if written
then reset
537 if written
then reset
543 redef class FlatString
545 redef fun insert_at
(s
, pos
) do
546 var l
= substring
(0, pos
)
547 var r
= substring_from
(pos
)
555 if slen
== 0 then return self
556 if mlen
== 0 then return s
557 var nlen
= slen
+ mlen
558 if s
isa FlatString then
559 if nlen
> maxlen
then return new Concat(self, s
)
561 var sifrom
= s
._first_byte
562 var mifrom
= _first_byte
564 var ns
= new NativeString(nlen
+ 1)
565 mits
.copy_to
(ns
, mlen
, mifrom
, 0)
566 sits
.copy_to
(ns
, slen
, sifrom
, mlen
)
567 return new FlatString.full
(ns
, nlen
, 0, length
+ s
.length
)
568 else if s
isa Concat then
570 var sllen
= sl
.bytelen
571 if sllen
+ mlen
> maxlen
then return new Concat(self, s
)
572 return new Concat(self + sl
, s
._right
)
579 # A simple linked list for use with iterators
580 private class RopeCharIteratorPiece
581 # The encapsulated node of the `Rope`
583 # Was its _left child (if any) visited ?
585 # Was its _right child (if any) visited ?
587 # The previous node in the list.
588 var prev
: nullable RopeCharIteratorPiece
591 # A reverse iterator capable of working with `Rope` objects
592 private class RopeByteReverseIterator
593 super IndexedIterator[Byte]
595 # Current NativeString
596 var ns
: NativeString is noautoinit
597 # Current position in NativeString
598 var pns
: Int is noautoinit
599 # Position in the Rope (0-indexed)
600 var pos
: Int is noautoinit
601 # Iterator on the substrings, does the Postfix part of
602 # the Rope traversal.
603 var subs
: IndexedIterator[FlatString] is noautoinit
605 init from
(root
: Concat, pos
: Int) do
607 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
610 pns
= pos
- subs
.index
613 redef fun index
do return pos
615 redef fun is_ok
do return pos
>= 0
617 redef fun item
do return ns
[pns
]
622 if pns
>= 0 then return
623 if not subs
.is_ok
then return
625 if not subs
.is_ok
then return
632 # Forward iterator on the bytes of a `Rope`
633 private class RopeByteIterator
634 super IndexedIterator[Byte]
636 # Position in current `String`
637 var pns
: Int is noautoinit
638 # Current `String` being iterated on
639 var ns
: NativeString is noautoinit
640 # Substrings of the Rope
641 var subs
: IndexedIterator[FlatString] is noautoinit
642 # Maximum position to iterate on (e.g. Rope.length)
643 var max
: Int is noautoinit
644 # Position (char) in the Rope (0-indexed)
645 var pos
: Int is noautoinit
647 init from
(root
: Concat, pos
: Int) do
648 subs
= new RopeSubstrings.from
(root
, pos
)
649 pns
= pos
- subs
.index
651 ns
= subs
.item
._items
652 max
= root
.length
- 1
655 redef fun item
do return ns
[pns
]
657 redef fun is_ok
do return pos
<= max
659 redef fun index
do return pos
664 if pns
< subs
.item
._bytelen
then return
665 if not subs
.is_ok
then return
667 if not subs
.is_ok
then return
668 ns
= subs
.item
._items
674 # A reverse iterator capable of working with `Rope` objects
675 private class RopeCharReverseIterator
676 super IndexedIterator[Char]
678 # Current NativeString
679 var ns
: String is noautoinit
680 # Current position in NativeString
681 var pns
: Int is noautoinit
682 # Position in the Rope (0-indexed)
683 var pos
: Int is noautoinit
684 # Iterator on the substrings, does the Postfix part of
685 # the Rope traversal.
686 var subs
: IndexedIterator[String] is noautoinit
688 init from
(root
: Concat, pos
: Int) do
690 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
692 pns
= pos
- subs
.index
695 redef fun index
do return pos
697 redef fun is_ok
do return pos
>= 0
699 redef fun item
do return ns
[pns
]
704 if pns
>= 0 then return
705 if not subs
.is_ok
then return
707 if not subs
.is_ok
then return
713 # Forward iterator on the chars of a `Rope`
714 private class RopeCharIterator
715 super IndexedIterator[Char]
717 # Position in current `String`
718 var pns
: Int is noautoinit
719 # Current `String` being iterated on
720 var str
: String is noautoinit
721 # Substrings of the Rope
722 var subs
: IndexedIterator[String] is noautoinit
723 # Maximum position to iterate on (e.g. Rope.length)
724 var max
: Int is noautoinit
725 # Position (char) in the Rope (0-indexed)
726 var pos
: Int is noautoinit
728 init from
(root
: Concat, pos
: Int) do
729 subs
= new RopeSubstrings.from
(root
, pos
)
730 pns
= pos
- subs
.index
733 max
= root
.length
- 1
736 redef fun item
do return str
[pns
]
738 redef fun is_ok
do return pos
<= max
740 redef fun index
do return pos
745 if pns
< subs
.item
.length
then return
746 if not subs
.is_ok
then return
748 if not subs
.is_ok
then return
754 # Substrings of a Rope (i.e. Reverse postfix iterator on leaves)
755 private class ReverseRopeSubstrings
756 super IndexedIterator[FlatString]
759 var iter
: RopeCharIteratorPiece is noinit
761 var pos
: Int is noinit
764 var str
: FlatString is noinit
766 init from
(root
: Concat, pos
: Int) do
767 var r
= new RopeCharIteratorPiece(root
, false, true, null)
768 var rnod
: String = root
771 if rnod
isa Concat then
772 if off
>= rnod
._left
.length
then
773 off
-= rnod
._left
.length
775 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
779 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
782 str
= rnod
.as(FlatString)
791 redef fun item
do return str
793 redef fun index
do return pos
795 redef fun is_ok
do return pos
>= 0
798 if pos
< 0 then return
800 var currit
= curr
.node
801 while curr
!= null do
803 if not currit
isa Concat then
804 str
= currit
.as(FlatString)
809 if not curr
.rdone
then
811 curr
= new RopeCharIteratorPiece(currit
._right
, false, false, curr
)
814 if not curr
.ldone
then
816 curr
= new RopeCharIteratorPiece(currit
._left
, false, false, curr
)
825 private class RopeBufSubstringIterator
826 super Iterator[FlatText]
828 # Iterator on the substrings of the building string
829 var iter
: Iterator[FlatText] is noautoinit
830 # Makes a String out of the buffered part of the Ropebuffer
831 var nsstr
: FlatString is noautoinit
832 # Did we attain the buffered part ?
833 var nsstr_done
= false
835 init from
(str
: RopeBuffer) do
836 iter
= str
.str
.substrings
837 nsstr
= new FlatString.with_infos
(str
.ns
, str
.rpos
- str
.dumped
, str
.dumped
)
838 if str
.length
== 0 then nsstr_done
= true
841 redef fun is_ok
do return iter
.is_ok
or not nsstr_done
845 if iter
.is_ok
then return iter
.item
858 # Substrings of a Rope (i.e. Postfix iterator on leaves)
859 private class RopeSubstrings
860 super IndexedIterator[FlatString]
863 var iter
: RopeCharIteratorPiece is noinit
865 var pos
: Int is noinit
866 # Maximum position in `Rope` (i.e. length - 1)
867 var max
: Int is noinit
870 var str
: FlatString is noinit
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
962 if lft
.bytelen
>= i
then
970 redef fun iterator_from
(i
) do return new RopeByteIterator.from
(target
, i
)
972 redef fun reverse_iterator_from
(i
) do return new RopeByteReverseIterator.from
(target
, i
)
976 # An Iterator over a RopeBuffer.
977 class RopeBufferCharIterator
978 super IndexedIterator[Char]
981 var sit
: IndexedIterator[Char] is noautoinit
983 redef fun index
do return sit
.index
985 # Init the iterator from a RopeBuffer starting from `pos`.
986 init from
(t
: RopeBuffer, pos
: Int) do
988 sit
= t
.str
.chars
.iterator_from
(pos
)
991 redef fun is_ok
do return sit
.is_ok
998 redef fun next
do sit
.next
1001 # Reverse iterator over a RopeBuffer.
1002 class RopeBufferCharReverseIterator
1003 super IndexedIterator[Char]
1006 var sit
: IndexedIterator[Char] is noautoinit
1008 redef fun index
do return sit
.index
1010 # Init the iterator from a RopeBuffer starting from `pos`.
1011 init from
(tgt
: RopeBuffer, pos
: Int) do
1013 sit
= tgt
.str
.chars
.reverse_iterator_from
(pos
)
1016 redef fun is_ok
do return sit
.is_ok
1023 redef fun next
do sit
.next
1026 # View on the chars of a `RopeBuffer`
1027 class RopeBufferChars
1028 super BufferCharView
1030 redef type SELFTYPE: RopeBuffer
1032 redef fun [](i
) do return target
[i
]
1034 redef fun []=(i
,c
) do target
[i
] = c
1036 redef fun add
(c
) do target
.add c
1038 redef fun push
(c
) do target
.add c
1040 redef fun iterator_from
(i
) do return new RopeBufferCharIterator.from
(target
, i
)
1042 redef fun reverse_iterator_from
(i
) do return new RopeBufferCharReverseIterator.from
(target
, i
)
1045 # An Iterator over a RopeBuffer.
1046 class RopeBufferByteIterator
1047 super IndexedIterator[Byte]
1050 var sit
: IndexedIterator[Byte] is noautoinit
1052 # Native string iterated over.
1053 var ns
: NativeString is noautoinit
1055 # Current position in `ns`.
1056 var pns
: Int is noautoinit
1058 # Maximum position iterable.
1059 var maxpos
: Int is noautoinit
1061 redef var index
is noautoinit
1063 # Init the iterator from a RopeBuffer starting from `pos`.
1064 init from
(t
: RopeBuffer, pos
: Int) do
1067 sit
= t
.str
.bytes
.iterator_from
(pos
)
1068 pns
= pos
- t
.str
.length
1072 redef fun is_ok
do return index
< maxpos
1075 if sit
.is_ok
then return sit
.item
1089 # Reverse iterator over a RopeBuffer.
1090 class RopeBufferByteReverseIterator
1091 super IndexedIterator[Byte]
1094 var sit
: IndexedIterator[Byte] is noautoinit
1096 # Native string iterated over.
1097 var ns
: NativeString is noautoinit
1099 # Current position in `ns`.
1100 var pns
: Int is noautoinit
1102 redef var index
is noautoinit
1104 # Init the iterator from a RopeBuffer starting from `pos`.
1105 init from
(tgt
: RopeBuffer, pos
: Int) do
1106 sit
= tgt
.str
.bytes
.reverse_iterator_from
(pos
- (tgt
.rpos
- tgt
.dumped
))
1107 pns
= pos
- tgt
.str
.bytelen
+ tgt
.rpos
1112 redef fun is_ok
do return index
>= 0
1115 if pns
>= 0 then return ns
[pns
]
1129 # View on the chars of a `RopeBuffer`
1130 class RopeBufferBytes
1131 super BufferByteView
1133 redef type SELFTYPE: RopeBuffer
1136 if i
< target
.str
.bytelen
then
1137 return target
.str
.bytes
[i
]
1139 return target
.ns
[i
- target
.str
.bytelen
]
1143 redef fun iterator_from
(i
) do return new RopeBufferByteIterator.from
(target
, i
)
1145 redef fun reverse_iterator_from
(i
) do return new RopeBufferByteReverseIterator.from
(target
, i
)