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 # 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(self)
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
+ len
< llen
then return lft
.substring
(from
,len
)
158 var lsublen
= llen
- from
159 return lft
.substring_from
(from
) + _right
.substring
(0, len
- lsublen
)
161 return _right
.substring
(from
- llen
, len
)
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(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_with_length
(rem
))
431 var nns
= new NativeString(count
)
432 ns
.copy_to
(nns
, count
, dumped
, 0)
433 return new RopeBuffer.from
(nns
.to_s_with_length
(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 if rp
>= buf_size
then
470 # TODO: Fix when supporting UTF-8
471 ns
[rp
] = c
.ascii
.to_b
477 # Converts the Buffer to a FlatString, appends it to
478 # the final String and re-allocates a new larger Buffer.
479 private fun dump_buffer
do
481 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
485 ns
= new NativeString(bs
)
491 # Similar to dump_buffer, but does not reallocate a new NativeString
492 private fun persist_buffer
do
493 if rpos
== dumped
then return
494 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
501 new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1).output
504 # Enlarge is useless here since the `Buffer`
505 # part is automatically dumped when necessary.
507 # Also, since the buffer can not be overused by a
508 # single string, there is no need for manual
511 # "You have no power here !"
512 redef fun enlarge
(i
) do end
521 # Flush the buffer in order to only have to reverse `str`.
522 if rpos
> 0 and dumped
!= rpos
then
523 str
+= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
530 if written
then reset
536 if written
then reset
542 redef class FlatString
544 redef fun insert_at
(s
, pos
) do
545 var l
= substring
(0, pos
)
546 var r
= substring_from
(pos
)
554 if slen
== 0 then return self
555 if mlen
== 0 then return s
556 var nlen
= slen
+ mlen
557 if s
isa FlatString then
558 if nlen
> maxlen
then return new Concat(self, s
)
560 var sifrom
= s
._first_byte
561 var mifrom
= _first_byte
563 var ns
= new NativeString(nlen
+ 1)
564 mits
.copy_to
(ns
, mlen
, mifrom
, 0)
565 sits
.copy_to
(ns
, slen
, sifrom
, mlen
)
566 return new FlatString.full
(ns
, nlen
, 0, nlen
- 1, length
+ s
.length
)
567 else if s
isa Concat then
569 var sllen
= sl
.bytelen
570 if sllen
+ mlen
> maxlen
then return new Concat(self, s
)
571 return new Concat(self + sl
, s
._right
)
578 # A simple linked list for use with iterators
579 private class RopeCharIteratorPiece
580 # The encapsulated node of the `Rope`
582 # Was its _left child (if any) visited ?
584 # Was its _right child (if any) visited ?
586 # The previous node in the list.
587 var prev
: nullable RopeCharIteratorPiece
590 # A reverse iterator capable of working with `Rope` objects
591 private class RopeByteReverseIterator
592 super IndexedIterator[Byte]
594 # Current NativeString
596 # Current position in NativeString
598 # Position in the Rope (0-indexed)
600 # Iterator on the substrings, does the Postfix part of
601 # the Rope traversal.
602 var subs
: IndexedIterator[FlatString]
604 init(root
: Concat) is old_style_init
do
605 pos
= root
._bytelen
- 1
606 subs
= new ReverseRopeSubstrings(root
)
612 init from
(root
: Concat, pos
: Int) do
614 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
617 pns
= pos
- subs
.index
620 redef fun index
do return pos
622 redef fun is_ok
do return pos
>= 0
624 redef fun item
do return ns
[pns
]
629 if pns
>= 0 then return
630 if not subs
.is_ok
then return
632 if not subs
.is_ok
then return
639 # Forward iterator on the bytes of a `Rope`
640 private class RopeByteIterator
641 super IndexedIterator[Byte]
643 # Position in current `String`
645 # Current `String` being iterated on
647 # Substrings of the Rope
648 var subs
: IndexedIterator[FlatString]
649 # Maximum position to iterate on (e.g. Rope.length)
651 # Position (char) in the Rope (0-indexed)
654 init(root
: Concat) is old_style_init
do
655 subs
= new RopeSubstrings(root
)
657 ns
= subs
.item
._items
658 max
= root
.length
- 1
662 init from
(root
: Concat, pos
: Int) do
663 subs
= new RopeSubstrings.from
(root
, pos
)
664 pns
= pos
- subs
.index
666 ns
= subs
.item
._items
667 max
= root
.length
- 1
670 redef fun item
do return ns
[pns
]
672 redef fun is_ok
do return pos
<= max
674 redef fun index
do return pos
679 if pns
< subs
.item
._bytelen
then return
680 if not subs
.is_ok
then return
682 if not subs
.is_ok
then return
683 ns
= subs
.item
._items
689 # A reverse iterator capable of working with `Rope` objects
690 private class RopeCharReverseIterator
691 super IndexedIterator[Char]
693 # Current NativeString
695 # Current position in NativeString
697 # Position in the Rope (0-indexed)
699 # Iterator on the substrings, does the Postfix part of
700 # the Rope traversal.
701 var subs
: IndexedIterator[String]
703 init(root
: Concat) is old_style_init
do
704 pos
= root
.length
- 1
705 subs
= new ReverseRopeSubstrings(root
)
710 init from
(root
: Concat, pos
: Int) do
712 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
714 pns
= pos
- subs
.index
717 redef fun index
do return pos
719 redef fun is_ok
do return pos
>= 0
721 redef fun item
do return ns
[pns
]
726 if pns
>= 0 then return
727 if not subs
.is_ok
then return
729 if not subs
.is_ok
then return
735 # Forward iterator on the chars of a `Rope`
736 private class RopeCharIterator
737 super IndexedIterator[Char]
739 # Position in current `String`
741 # Current `String` being iterated on
743 # Substrings of the Rope
744 var subs
: IndexedIterator[String]
745 # Maximum position to iterate on (e.g. Rope.length)
747 # Position (char) in the Rope (0-indexed)
750 init(root
: Concat) is old_style_init
do
751 subs
= new RopeSubstrings(root
)
754 max
= root
.length
- 1
758 init from
(root
: Concat, pos
: Int) do
759 subs
= new RopeSubstrings.from
(root
, pos
)
760 pns
= pos
- subs
.index
763 max
= root
.length
- 1
766 redef fun item
do return str
[pns
]
768 redef fun is_ok
do return pos
<= max
770 redef fun index
do return pos
775 if pns
< subs
.item
.length
then return
776 if not subs
.is_ok
then return
778 if not subs
.is_ok
then return
784 # Substrings of a Rope (i.e. Reverse postfix iterator on leaves)
785 private class ReverseRopeSubstrings
786 super IndexedIterator[FlatString]
789 var iter
: RopeCharIteratorPiece is noinit
791 var pos
: Int is noinit
794 var str
: FlatString is noinit
796 init(root
: Concat) is old_style_init
do
797 var r
= new RopeCharIteratorPiece(root
, false, true, null)
798 pos
= root
.length
- 1
799 var lnod
: String = root
801 if lnod
isa Concat then
803 r
= new RopeCharIteratorPiece(lnod
, false, true, r
)
805 str
= lnod
.as(FlatString)
812 init from
(root
: Concat, pos
: Int) do
813 var r
= new RopeCharIteratorPiece(root
, false, true, null)
814 var rnod
: String = root
817 if rnod
isa Concat then
818 if off
>= rnod
._left
.length
then
819 off
-= rnod
._left
.length
821 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
825 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
828 str
= rnod
.as(FlatString)
837 redef fun item
do return str
839 redef fun index
do return pos
841 redef fun is_ok
do return pos
>= 0
844 if pos
< 0 then return
846 var currit
= curr
.node
847 while curr
!= null do
849 if not currit
isa Concat then
850 str
= currit
.as(FlatString)
855 if not curr
.rdone
then
857 curr
= new RopeCharIteratorPiece(currit
._right
, false, false, curr
)
860 if not curr
.ldone
then
862 curr
= new RopeCharIteratorPiece(currit
._left
, false, false, curr
)
871 private class RopeBufSubstringIterator
872 super Iterator[FlatText]
874 # Iterator on the substrings of the building string
875 var iter
: Iterator[FlatText]
876 # Makes a String out of the buffered part of the Ropebuffer
877 var nsstr
: FlatString
878 # Did we attain the buffered part ?
879 var nsstr_done
= false
881 init(str
: RopeBuffer) is old_style_init
do
882 iter
= str
.str
.substrings
883 nsstr
= new FlatString.with_infos
(str
.ns
, str
.rpos
- str
.dumped
, str
.dumped
, str
.rpos
- 1)
884 if str
.length
== 0 then nsstr_done
= true
887 redef fun is_ok
do return iter
.is_ok
or not nsstr_done
891 if iter
.is_ok
then return iter
.item
904 # Substrings of a Rope (i.e. Postfix iterator on leaves)
905 private class RopeSubstrings
906 super IndexedIterator[FlatString]
909 var iter
: RopeCharIteratorPiece is noinit
911 var pos
: Int is noinit
912 # Maximum position in `Rope` (i.e. length - 1)
913 var max
: Int is noinit
916 var str
: FlatString is noinit
918 init(root
: Concat) is old_style_init
do
919 var r
= new RopeCharIteratorPiece(root
, true, false, null)
921 max
= root
.length
- 1
922 var rnod
: String = root
924 if rnod
isa Concat then
926 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
928 str
= rnod
.as(FlatString)
936 init from
(root
: Concat, pos
: Int) do
937 var r
= new RopeCharIteratorPiece(root
, true, false, null)
938 max
= root
.length
- 1
939 var rnod
: String = root
942 if rnod
isa Concat then
943 if off
>= rnod
._left
.length
then
945 off
-= rnod
._left
.length
947 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
950 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
953 str
= rnod
.as(FlatString)
962 redef fun item
do return str
964 redef fun is_ok
do return pos
<= max
966 redef fun index
do return pos
970 if pos
> max
then return
974 if not rnod
isa Concat then
977 str
= rnod
.as(FlatString)
978 iter
= it
.as(not null)
984 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
985 else if not it
.rdone
then
988 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
998 # Implementation of a `StringCharView` for `Concat` objects
999 private class RopeChars
1000 super StringCharView
1002 redef type SELFTYPE: Concat
1008 redef fun iterator_from
(i
) do return new RopeCharIterator.from
(target
, i
)
1010 redef fun reverse_iterator_from
(i
) do return new RopeCharReverseIterator.from
(target
, i
)
1014 # Implementation of a `StringCharView` for `Concat` objects
1015 private class RopeBytes
1016 super StringByteView
1018 redef type SELFTYPE: Concat
1021 var nod
: String = target
1023 if nod
isa FlatString then return nod
._items
[i
]
1024 if not nod
isa Concat then abort
1026 if lft
.bytelen
>= i
then
1034 redef fun iterator_from
(i
) do return new RopeByteIterator.from
(target
, i
)
1036 redef fun reverse_iterator_from
(i
) do return new RopeByteReverseIterator.from
(target
, i
)
1040 # An Iterator over a RopeBuffer.
1041 class RopeBufferCharIterator
1042 super IndexedIterator[Char]
1045 var sit
: IndexedIterator[Char]
1047 redef fun index
do return sit
.index
1049 # Init the iterator from a RopeBuffer.
1050 init(t
: RopeBuffer) is old_style_init
do
1052 sit
= t
.str
.chars
.iterator
1055 # Init the iterator from a RopeBuffer starting from `pos`.
1056 init from
(t
: RopeBuffer, pos
: Int) do
1058 sit
= t
.str
.chars
.iterator_from
(pos
)
1061 redef fun is_ok
do return sit
.is_ok
1068 redef fun next
do sit
.next
1071 # Reverse iterator over a RopeBuffer.
1072 class RopeBufferCharReverseIterator
1073 super IndexedIterator[Char]
1076 var sit
: IndexedIterator[Char]
1078 redef fun index
do return sit
.index
1080 # Init the iterator from a RopeBuffer.
1081 init(tgt
: RopeBuffer) is old_style_init
do
1083 sit
= tgt
.str
.chars
.reverse_iterator
1086 # Init the iterator from a RopeBuffer starting from `pos`.
1087 init from
(tgt
: RopeBuffer, pos
: Int) do
1089 sit
= tgt
.str
.chars
.reverse_iterator_from
(pos
)
1092 redef fun is_ok
do return sit
.is_ok
1099 redef fun next
do sit
.next
1102 # View on the chars of a `RopeBuffer`
1103 class RopeBufferChars
1104 super BufferCharView
1106 redef type SELFTYPE: RopeBuffer
1108 redef fun [](i
) do return target
[i
]
1110 redef fun []=(i
,c
) do target
[i
] = c
1112 redef fun add
(c
) do target
.add c
1114 redef fun push
(c
) do target
.add c
1116 redef fun iterator_from
(i
) do return new RopeBufferCharIterator.from
(target
, i
)
1118 redef fun reverse_iterator_from
(i
) do return new RopeBufferCharReverseIterator.from
(target
, i
)
1121 # An Iterator over a RopeBuffer.
1122 class RopeBufferByteIterator
1123 super IndexedIterator[Byte]
1126 var sit
: IndexedIterator[Byte]
1128 # Native string iterated over.
1129 var ns
: NativeString
1131 # Current position in `ns`.
1134 # Maximum position iterable.
1139 # Init the iterator from a RopeBuffer.
1140 init(t
: RopeBuffer) is old_style_init
do
1143 sit
= t
.str
.bytes
.iterator
1148 # Init the iterator from a RopeBuffer starting from `pos`.
1149 init from
(t
: RopeBuffer, pos
: Int) do
1152 sit
= t
.str
.bytes
.iterator_from
(pos
)
1153 pns
= pos
- t
.str
.length
1157 redef fun is_ok
do return index
< maxpos
1160 if sit
.is_ok
then return sit
.item
1174 # Reverse iterator over a RopeBuffer.
1175 class RopeBufferByteReverseIterator
1176 super IndexedIterator[Byte]
1179 var sit
: IndexedIterator[Byte]
1181 # Native string iterated over.
1182 var ns
: NativeString
1184 # Current position in `ns`.
1189 # Init the iterator from a RopeBuffer.
1190 init(tgt
: RopeBuffer) is old_style_init
do
1191 sit
= tgt
.str
.bytes
.reverse_iterator
1193 index
= tgt
._bytelen
- 1
1197 # Init the iterator from a RopeBuffer starting from `pos`.
1198 init from
(tgt
: RopeBuffer, pos
: Int) do
1199 sit
= tgt
.str
.bytes
.reverse_iterator_from
(pos
- (tgt
.rpos
- tgt
.dumped
))
1200 pns
= pos
- tgt
.str
.bytelen
+ tgt
.rpos
1205 redef fun is_ok
do return index
>= 0
1208 if pns
>= 0 then return ns
[pns
]
1222 # View on the chars of a `RopeBuffer`
1223 class RopeBufferBytes
1224 super BufferByteView
1226 redef type SELFTYPE: RopeBuffer
1229 if i
< target
.str
.bytelen
then
1230 return target
.str
.bytes
[i
]
1232 return target
.ns
[i
- target
.str
.bytelen
]
1236 redef fun iterator_from
(i
) do return new RopeBufferByteIterator.from
(target
, i
)
1238 redef fun reverse_iterator_from
(i
) do return new RopeBufferByteReverseIterator.from
(target
, i
)