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 512
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 fun chars
do return new RopeChars(self)
75 redef fun bytes
do return new RopeBytes(self)
77 redef var length
is noinit
79 redef var byte_length
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
: FlatString is noinit
88 # Position of the beginning of `flat_cache` in `self`
89 var flat_last_pos_start
: Int is noinit
91 redef fun to_cstring
do
92 var len
= _byte_length
93 var ns
= new NativeString(len
+ 1)
96 for i
in substrings
do
97 var ilen
= i
._byte_length
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 _byte_length
= l
.byte_length
+ r
.byte_length
114 _flat_last_pos_start
= _length
117 redef fun is_empty
do return _byte_length
== 0
124 redef fun iterator
do return new RopeCharIterator.from
(self, 0)
128 for j
in [1 .. i
[ do x
+= self
133 assert i
>= 0 and i
< _length
134 var flps
= _flat_last_pos_start
137 if i
< flps
+ fc
._length
then return fc
.fetch_char_at
(i
- flps
)
139 var lf
= get_leaf_at
(i
)
140 return lf
.fetch_char_at
(i
- _flat_last_pos_start
)
143 fun get_leaf_at
(pos
: Int): FlatString do
144 var flps
= _flat_last_pos_start
147 if pos
< flps
+ fc
._length
then return fc
152 if s
isa FlatString then break
155 var llen
= lft
.length
163 _flat_last_pos_start
= st
- pos
168 redef fun substring
(from
, count
) do
171 if count
< 0 then return ""
176 if (count
+ from
) > ln
then count
= ln
- from
177 if count
<= 0 then return ""
178 var end_index
= from
+ count
- 1
180 var flps
= _flat_last_pos_start
183 if end_index
< flps
+ fc
._length
then
184 return fc
.substring_impl
(from
- flps
, count
, end_index
- flps
)
189 var llen
= lft
.length
191 if from
+ count
< llen
then return lft
.substring
(from
, count
)
192 var lsublen
= llen
- from
193 return lft
.substring_from
(from
) + _right
.substring
(0, count
- lsublen
)
195 return _right
.substring
(from
- llen
, count
)
199 redef fun reversed
do return new Concat(_right
.reversed
, _left
.reversed
)
201 redef fun insert_at
(s
, pos
) do
203 if pos
> lft
.length
then
204 return lft
+ _right
.insert_at
(s
, pos
- lft
.length
)
206 return lft
.insert_at
(s
, pos
) + _right
209 redef fun to_upper
do return new Concat(_left
.to_upper
, _right
.to_upper
)
211 redef fun to_lower
do return new Concat(_left
.to_lower
, _right
.to_lower
)
215 var slen
= s
.byte_length
217 return new Concat(self, s
)
220 var rlen
= r
.byte_length
221 if rlen
+ slen
> maxlen
then return new Concat(self, s
)
222 return new Concat(_left
, r
+ s
)
226 redef fun copy_to_native
(dest
, n
, src_offset
, dest_offset
) do
228 if src_offset
< l
.byte_length
then
229 var lcopy
= l
.byte_length
- src_offset
230 lcopy
= if lcopy
> n
then n
else lcopy
231 l
.copy_to_native
(dest
, lcopy
, src_offset
, dest_offset
)
236 _right
.copy_to_native
(dest
, n
, src_offset
, dest_offset
)
239 # Returns a balanced version of `self`
240 fun balance
: String do
241 var children
= new Array[String]
243 var iter
: nullable RopeCharIteratorPiece = new RopeCharIteratorPiece(self, false, false, null)
245 if iter
== null then break
247 if not rnod
isa Concat then
252 if not iter
.ldone
then
254 iter
= new RopeCharIteratorPiece(rnod
._left
, false, false, iter
)
255 else if not iter
.rdone
then
257 iter
= new RopeCharIteratorPiece(rnod
._right
, false, false, iter
)
263 return recurse_balance
(children
, children
.length
)
266 fun recurse_balance
(nodes
: Array[String], len
: Int): String do
270 if len
- stpos
> 1 then
271 nodes
[finpos
] = new Concat(nodes
[stpos
], nodes
[stpos
+ 1])
274 nodes
[finpos
] = nodes
[stpos
]
279 if finpos
== 1 then return nodes
[0]
280 return recurse_balance
(nodes
, finpos
)
284 # Mutable `Rope`, optimized for concatenation operations
286 # A `RopeBuffer` is an efficient way of building a `String` when
287 # concatenating small strings.
289 # It does concatenations in an optimized way by having a
290 # mutable part and an immutable part built by efficiently
291 # concatenating strings in chain.
293 # Every concatenation operation is done by copying a string to
294 # the mutable part and flushing it when full.
296 # However, when a long string is appended to the `Buffer`,
297 # the concatenation is done at it would be in a `Rope`.
302 redef fun chars
do return new RopeBufferChars(self)
304 redef fun bytes
do return new RopeBufferBytes(self)
306 # The final string being built on the fly
307 private var str
: String = ""
309 # Current concatenation buffer
310 private var ns
: NativeString is noinit
312 # Next available (e.g. unset) character in the `Buffer`
315 # Length (in chars) of the buffered part
316 private var nslen
= 0
318 # Keeps track of the buffer's currently dumped part
320 # This might happen if for instance, a String was being
321 # built by concatenating small parts of string and suddenly
322 # a long string (length > maxlen) is appended.
323 private var dumped
: Int is noinit
325 # Length of the complete rope in chars (0)
336 # Length of the complete rope in bytes
337 redef var byte_length
= 0
339 # Length of the mutable part (in bytes)
341 # Is also used as base to compute the size of the next
342 # mutable native string (`ns`)
343 private var buf_size
: Int is noinit
345 redef fun substrings
do return new RopeBufSubstringIterator.from
(self)
347 # Builds an empty `RopeBuffer`
349 ns
= new NativeString(maxlen
)
354 # Builds a new `RopeBuffer` with `str` in it.
355 init from
(str
: String) do
357 ns
= new NativeString(maxlen
)
359 _byte_length
= str
.length
363 # Resets the informations of the `Buffer`
365 # This is called when doing in-place modifications
366 # on a previously to_s'd `RopeBuffer`
368 var nns
= new NativeString(buf_size
)
369 var blen
= rpos
- dumped
370 ns
.copy_to
(nns
, blen
, dumped
, 0)
378 if i
< str
.length
then
381 var index
= ns
.char_to_byte_index_cached
(i
- str
.length
, 0, dumped
)
382 return ns
.char_at
(index
)
386 redef fun []=(i
, c
) do
387 assert i
>= 0 and i
<= length
388 if i
== length
then add c
389 if i
< str
.length
then
390 _byte_length
+= c
.u8char_len
- str
[i
].u8char_len
392 var l
= s
.substring
(0, i
)
393 var r
= s
.substring_from
(i
+ 1)
396 var reali
= i
- str
.length
397 var index
= ns
.char_to_byte_index_cached
(reali
, 0, dumped
)
398 var st_nxt
= ns
.char_to_byte_index_cached
(reali
+ 1, reali
, index
)
399 var loc_c
= ns
.char_at
(index
)
400 if loc_c
.u8char_len
!= c
.u8char_len
then
401 var delta
= c
.u8char_len
- loc_c
.u8char_len
402 var remsp
= buf_size
- rpos
403 if remsp
< delta
then
405 var nns
= new NativeString(buf_size
)
406 ns
.copy_to
(nns
, index
- dumped
, dumped
, 0)
407 ns
.copy_to
(nns
, rpos
- index
- loc_c
.u8char_len
, index
+ loc_c
.u8char_len
, index
- dumped
+ delta
)
409 index
= index
- dumped
411 ns
.copy_to
(ns
, rpos
- st_nxt
, st_nxt
, st_nxt
+ delta
)
413 _byte_length
+= delta
416 ns
.set_char_at
(index
, c
)
420 redef fun empty
do return new RopeBuffer
428 ns
= new NativeString(buf_size
)
433 redef fun substring
(from
, count
) do
434 var strlen
= str
.length
438 if count
< 0 then count
= 0
442 if count
> length
then count
= length
- from
444 if count
== 0 then return empty
446 if from
< strlen
then
447 var subpos
= strlen
- from
448 if count
<= subpos
then
449 return new RopeBuffer.from
(str
.substring
(from
, count
))
451 var l
= str
.substring_from
(from
)
452 var rem
= count
- subpos
453 var nns
= new NativeString(rem
)
454 ns
.copy_to
(nns
, rem
, dumped
, 0)
455 return new RopeBuffer.from
(l
+ nns
.to_s_unsafe
(rem
))
458 var nns
= new NativeString(count
)
459 ns
.copy_to
(nns
, count
, dumped
, 0)
460 return new RopeBuffer.from
(nns
.to_s_unsafe
(count
))
464 redef fun append
(s
) do
465 var slen
= s
.byte_length
466 if slen
>= maxlen
then
471 if s
isa FlatText then
473 var from
= s
.first_byte
474 var remsp
= buf_size
- rpos
475 if slen
<= remsp
then
476 oits
.copy_to
(ns
, slen
, from
, rpos
)
480 var brk
= oits
.find_beginning_of_char_at
(from
+ remsp
)
481 oits
.copy_to
(ns
, brk
, from
, rpos
)
484 oits
.copy_to
(ns
, slen
- remsp
, brk
, 0)
487 for i
in s
.substrings
do append i
493 var remsp
= buf_size
- rp
494 var cln
= c
.u8char_len
499 ns
.set_char_at
(rp
, c
)
505 # Converts the Buffer to a FlatString, appends it to
506 # the final String and re-allocates a new larger Buffer.
507 private fun dump_buffer
do
509 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
)
513 ns
= new NativeString(bs
)
519 # Similar to dump_buffer, but does not reallocate a new NativeString
520 private fun persist_buffer
do
521 if rpos
== dumped
then return
522 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
)
529 new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
).output
532 # Enlarge is useless here since the `Buffer`
533 # part is automatically dumped when necessary.
535 # Also, since the buffer can not be overused by a
536 # single string, there is no need for manual
539 # "You have no power here !"
540 redef fun enlarge
(i
) do end
549 # Flush the buffer in order to only have to reverse `str`.
550 if rpos
> 0 and dumped
!= rpos
then
551 str
+= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
)
558 if written
then reset
564 if written
then reset
570 redef class FlatString
572 redef fun insert_at
(s
, pos
) do
573 var l
= substring
(0, pos
)
574 var r
= substring_from
(pos
)
580 var slen
= s
.byte_length
581 var mlen
= _byte_length
582 if slen
== 0 then return self
583 if mlen
== 0 then return s
584 var nlen
= slen
+ mlen
585 if s
isa FlatString then
586 if nlen
> maxlen
then return new Concat(self, s
)
588 var sifrom
= s
._first_byte
589 var mifrom
= _first_byte
591 var ns
= new NativeString(nlen
+ 1)
592 mits
.copy_to
(ns
, mlen
, mifrom
, 0)
593 sits
.copy_to
(ns
, slen
, sifrom
, mlen
)
594 return new FlatString.full
(ns
, nlen
, 0, length
+ s
.length
)
595 else if s
isa Concat then
597 var sllen
= sl
.byte_length
598 if sllen
+ mlen
> maxlen
then return new Concat(self, s
)
599 return new Concat(self + sl
, s
._right
)
606 # A simple linked list for use with iterators
607 private class RopeCharIteratorPiece
608 # The encapsulated node of the `Rope`
610 # Was its _left child (if any) visited ?
612 # Was its _right child (if any) visited ?
614 # The previous node in the list.
615 var prev
: nullable RopeCharIteratorPiece
618 # A reverse iterator capable of working with `Rope` objects
619 private class RopeByteReverseIterator
620 super IndexedIterator[Byte]
622 # Current NativeString
623 var ns
: NativeString is noautoinit
624 # Current position in NativeString
625 var pns
: Int is noautoinit
626 # Position in the Rope (0-indexed)
627 var pos
: Int is noautoinit
628 # Iterator on the substrings, does the Postfix part of
629 # the Rope traversal.
630 var subs
: IndexedIterator[FlatString] is noautoinit
632 init from
(root
: Concat, pos
: Int) do
634 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
637 pns
= pos
- subs
.index
640 redef fun index
do return pos
642 redef fun is_ok
do return pos
>= 0
644 redef fun item
do return ns
[pns
]
649 if pns
>= 0 then return
650 if not subs
.is_ok
then return
652 if not subs
.is_ok
then return
659 # Forward iterator on the bytes of a `Rope`
660 private class RopeByteIterator
661 super IndexedIterator[Byte]
663 # Position in current `String`
664 var pns
: Int is noautoinit
665 # Current `String` being iterated on
666 var ns
: NativeString is noautoinit
667 # Substrings of the Rope
668 var subs
: IndexedIterator[FlatString] is noautoinit
669 # Maximum position to iterate on (e.g. Rope.byte_length)
670 var max
: Int is noautoinit
671 # Position (char) in the Rope (0-indexed)
672 var pos
: Int is noautoinit
674 init from
(root
: Concat, pos
: Int) do
675 subs
= new RopeSubstrings.from
(root
, pos
)
676 pns
= pos
- subs
.index
678 ns
= subs
.item
._items
679 max
= root
.byte_length
- 1
682 redef fun item
do return ns
[pns
]
684 redef fun is_ok
do return pos
<= max
686 redef fun index
do return pos
691 if pns
< subs
.item
._byte_length
then return
692 if not subs
.is_ok
then return
694 if not subs
.is_ok
then return
695 ns
= subs
.item
._items
701 # A reverse iterator capable of working with `Rope` objects
702 private class RopeCharReverseIterator
703 super IndexedIterator[Char]
705 # Current NativeString
706 var ns
: String is noautoinit
707 # Current position in NativeString
708 var pns
: Int is noautoinit
709 # Position in the Rope (0-indexed)
710 var pos
: Int is noautoinit
711 # Iterator on the substrings, does the Postfix part of
712 # the Rope traversal.
713 var subs
: IndexedIterator[String] is noautoinit
715 init from
(root
: Concat, pos
: Int) do
717 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
719 pns
= pos
- subs
.index
722 redef fun index
do return pos
724 redef fun is_ok
do return pos
>= 0
726 redef fun item
do return ns
[pns
]
731 if pns
>= 0 then return
732 if not subs
.is_ok
then return
734 if not subs
.is_ok
then return
740 # Forward iterator on the chars of a `Rope`
741 private class RopeCharIterator
742 super IndexedIterator[Char]
744 # Position in current `String`
745 var pns
: Int is noautoinit
746 # Current `String` being iterated on
747 var str
: String is noautoinit
748 # Substrings of the Rope
749 var subs
: IndexedIterator[String] is noautoinit
750 # Maximum position to iterate on (e.g. Rope.length)
751 var max
: Int is noautoinit
752 # Position (char) in the Rope (0-indexed)
753 var pos
: Int is noautoinit
755 init from
(root
: Concat, pos
: Int) do
756 subs
= new RopeSubstrings.from
(root
, pos
)
757 pns
= pos
- subs
.index
760 max
= root
.length
- 1
763 redef fun item
do return str
[pns
]
765 redef fun is_ok
do return pos
<= max
767 redef fun index
do return pos
772 if pns
< subs
.item
.length
then return
773 if not subs
.is_ok
then return
775 if not subs
.is_ok
then return
781 # Substrings of a Rope (i.e. Reverse postfix iterator on leaves)
782 private class ReverseRopeSubstrings
783 super IndexedIterator[FlatString]
786 var iter
: RopeCharIteratorPiece is noinit
788 var pos
: Int is noinit
791 var str
: FlatString is noinit
793 init from
(root
: Concat, pos
: Int) do
794 var r
= new RopeCharIteratorPiece(root
, false, true, null)
795 var rnod
: String = root
798 if rnod
isa Concat then
799 if off
>= rnod
._left
.length
then
800 off
-= rnod
._left
.length
802 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
806 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
809 str
= rnod
.as(FlatString)
818 redef fun item
do return str
820 redef fun index
do return pos
822 redef fun is_ok
do return pos
>= 0
825 if pos
< 0 then return
827 var currit
= curr
.as(not null).node
828 while curr
!= null do
830 if not currit
isa Concat then
831 str
= currit
.as(FlatString)
836 if not curr
.rdone
then
838 curr
= new RopeCharIteratorPiece(currit
._right
, false, false, curr
)
841 if not curr
.ldone
then
843 curr
= new RopeCharIteratorPiece(currit
._left
, false, false, curr
)
852 private class RopeBufSubstringIterator
853 super Iterator[FlatText]
855 # Iterator on the substrings of the building string
856 var iter
: Iterator[FlatText] is noautoinit
857 # Makes a String out of the buffered part of the Ropebuffer
858 var nsstr
: FlatString is noautoinit
859 # Did we attain the buffered part ?
860 var nsstr_done
= false
862 init from
(str
: RopeBuffer) do
863 iter
= str
.str
.substrings
864 nsstr
= new FlatString.with_infos
(str
.ns
, str
.rpos
- str
.dumped
, str
.dumped
)
865 if str
.length
== 0 then nsstr_done
= true
868 redef fun is_ok
do return iter
.is_ok
or not nsstr_done
872 if iter
.is_ok
then return iter
.item
885 # Substrings of a Rope (i.e. Postfix iterator on leaves)
886 private class RopeSubstrings
887 super IndexedIterator[FlatString]
890 var iter
: RopeCharIteratorPiece is noinit
892 var pos
: Int is noinit
893 # Maximum position in `Rope` (i.e. length - 1)
894 var max
: Int is noinit
897 var str
: FlatString is noinit
899 init from
(root
: Concat, pos
: Int) do
900 var r
= new RopeCharIteratorPiece(root
, true, false, null)
901 max
= root
.length
- 1
902 var rnod
: String = root
905 if rnod
isa Concat then
906 if off
>= rnod
._left
.length
then
908 off
-= rnod
._left
.length
910 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
913 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
916 str
= rnod
.as(FlatString)
925 redef fun item
do return str
927 redef fun is_ok
do return pos
<= max
929 redef fun index
do return pos
933 if pos
> max
then return
934 var it
= iter
.prev
.as(not null)
937 if not rnod
isa Concat then
940 str
= rnod
.as(FlatString)
947 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
948 else if not it
.rdone
then
951 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
953 it
= it
.prev
.as(not null)
961 # Implementation of a `StringCharView` for `Concat` objects
962 private class RopeChars
965 redef type SELFTYPE: Concat
971 redef fun iterator_from
(i
) do return new RopeCharIterator.from
(target
, i
)
973 redef fun reverse_iterator_from
(i
) do return new RopeCharReverseIterator.from
(target
, i
)
977 # Implementation of a `StringCharView` for `Concat` objects
978 private class RopeBytes
981 redef type SELFTYPE: Concat
983 var cache
: FlatString is noinit
985 var cache_start
: Int = -1
987 var cache_end
: Int = -1
990 assert i
>= 0 and i
< target
._byte_length
991 var flps
= _cache_start
992 if i
>= flps
and i
<= _cache_end
then
993 return _cache
.bytes
[i
- flps
]
995 var lf
= get_leaf_at
(i
)
996 return lf
.bytes
[i
- _cache_start
]
999 fun get_leaf_at
(pos
: Int): FlatString do
1000 var flps
= _cache_start
1001 if pos
>= flps
and pos
<= _cache_end
then
1004 var s
: String = target
1007 if s
isa FlatString then break
1010 var llen
= lft
.byte_length
1018 _cache_start
= st
- pos
1019 _cache_end
= st
- pos
+ s
.byte_length
- 1
1024 redef fun iterator_from
(i
) do return new RopeByteIterator.from
(target
, i
)
1026 redef fun reverse_iterator_from
(i
) do return new RopeByteReverseIterator.from
(target
, i
)
1030 # An Iterator over a RopeBuffer.
1031 class RopeBufferCharIterator
1032 super IndexedIterator[Char]
1035 var sit
: IndexedIterator[Char] is noautoinit
1037 redef fun index
do return sit
.index
1039 # Init the iterator from a RopeBuffer starting from `pos`.
1040 init from
(t
: RopeBuffer, pos
: Int) do
1042 sit
= t
.str
.chars
.iterator_from
(pos
)
1045 redef fun is_ok
do return sit
.is_ok
1052 redef fun next
do sit
.next
1055 # Reverse iterator over a RopeBuffer.
1056 class RopeBufferCharReverseIterator
1057 super IndexedIterator[Char]
1060 var sit
: IndexedIterator[Char] is noautoinit
1062 redef fun index
do return sit
.index
1064 # Init the iterator from a RopeBuffer starting from `pos`.
1065 init from
(tgt
: RopeBuffer, pos
: Int) do
1067 sit
= tgt
.str
.chars
.reverse_iterator_from
(pos
)
1070 redef fun is_ok
do return sit
.is_ok
1077 redef fun next
do sit
.next
1080 # View on the chars of a `RopeBuffer`
1081 class RopeBufferChars
1082 super BufferCharView
1084 redef type SELFTYPE: RopeBuffer
1086 redef fun [](i
) do return target
[i
]
1088 redef fun []=(i
,c
) do target
[i
] = c
1090 redef fun add
(c
) do target
.add c
1092 redef fun push
(c
) do target
.add c
1094 redef fun iterator_from
(i
) do return new RopeBufferCharIterator.from
(target
, i
)
1096 redef fun reverse_iterator_from
(i
) do return new RopeBufferCharReverseIterator.from
(target
, i
)
1099 # An Iterator over a RopeBuffer.
1100 class RopeBufferByteIterator
1101 super IndexedIterator[Byte]
1104 var sit
: IndexedIterator[Byte] is noautoinit
1106 # Native string iterated over.
1107 var ns
: NativeString is noautoinit
1109 # Current position in `ns`.
1110 var pns
: Int is noautoinit
1112 # Maximum position iterable.
1113 var maxpos
: Int is noautoinit
1115 redef var index
is noautoinit
1117 # Init the iterator from a RopeBuffer starting from `pos`.
1118 init from
(t
: RopeBuffer, pos
: Int) do
1120 maxpos
= t
._byte_length
1121 sit
= t
.str
.bytes
.iterator_from
(pos
)
1122 pns
= pos
- t
.str
.length
1126 redef fun is_ok
do return index
< maxpos
1129 if sit
.is_ok
then return sit
.item
1143 # Reverse iterator over a RopeBuffer.
1144 class RopeBufferByteReverseIterator
1145 super IndexedIterator[Byte]
1148 var sit
: IndexedIterator[Byte] is noautoinit
1150 # Native string iterated over.
1151 var ns
: NativeString is noautoinit
1153 # Current position in `ns`.
1154 var pns
: Int is noautoinit
1156 redef var index
is noautoinit
1158 # Init the iterator from a RopeBuffer starting from `pos`.
1159 init from
(tgt
: RopeBuffer, pos
: Int) do
1160 sit
= tgt
.str
.bytes
.reverse_iterator_from
(pos
- (tgt
.rpos
- tgt
.dumped
))
1161 pns
= pos
- tgt
.str
.byte_length
+ tgt
.rpos
1166 redef fun is_ok
do return index
>= 0
1169 if pns
>= 0 then return ns
[pns
]
1183 # View on the chars of a `RopeBuffer`
1184 class RopeBufferBytes
1185 super BufferByteView
1187 redef type SELFTYPE: RopeBuffer
1190 if i
< target
.str
.byte_length
then
1191 return target
.str
.bytes
[i
]
1193 return target
.ns
[i
- target
.str
.byte_length
]
1197 redef fun iterator_from
(i
) do return new RopeBufferByteIterator.from
(target
, i
)
1199 redef fun reverse_iterator_from
(i
) do return new RopeBufferByteReverseIterator.from
(target
, i
)