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
110 length
= left
.length
+ right
.length
111 bytelen
= left
.bytelen
+ right
.bytelen
119 redef fun iterator
do return new RopeCharIterator(self)
123 for j
in [1 .. i
[ do x
+= self
128 if flat_last_pos_start
!= -1 then
129 var fsp
= i
- flat_last_pos_start
130 if fsp
>= 0 and fsp
< flat_cache
.length
then return flat_cache
[fsp
]
135 if s
isa FlatString then break
138 var llen
= lft
.length
146 flat_last_pos_start
= st
- i
151 redef fun substring
(from
, len
) do
152 var llen
= left
.length
154 if from
+ len
< llen
then return left
.substring
(from
,len
)
155 var lsublen
= llen
- from
156 return left
.substring_from
(from
) + right
.substring
(0, len
- lsublen
)
158 return right
.substring
(from
- llen
, len
)
162 redef fun reversed
do return new Concat(right
.reversed
, left
.reversed
)
164 redef fun insert_at
(s
, pos
) do
165 if pos
> left
.length
then
166 return left
+ right
.insert_at
(s
, pos
- left
.length
)
168 return left
.insert_at
(s
, pos
) + right
171 redef fun to_upper
do return new Concat(left
.to_upper
, right
.to_upper
)
173 redef fun to_lower
do return new Concat(left
.to_lower
, right
.to_lower
)
179 return new Concat(self, s
)
183 if rlen
+ slen
> maxlen
then return new Concat(self, s
)
184 return new Concat(left
, r
+ s
)
188 redef fun copy_to_native
(dest
, n
, src_offset
, dest_offset
) do
189 var subs
= new RopeSubstrings.from
(self, src_offset
)
190 var st
= src_offset
- subs
.pos
191 var off
= dest_offset
194 if n
> it
.length
then
195 var cplen
= it
.length
- st
196 it
.items
.copy_to
(dest
, cplen
, st
, off
)
200 it
.items
.copy_to
(dest
, n
, st
, off
)
209 # Mutable `Rope`, optimized for concatenation operations
211 # A `RopeBuffer` is an efficient way of building a `String` when
212 # concatenating small strings.
214 # It does concatenations in an optimized way by having a
215 # mutable part and an immutable part built by efficiently
216 # concatenating strings in chain.
218 # Every concatenation operation is done by copying a string to
219 # the mutable part and flushing it when full.
221 # However, when a long string is appended to the `Buffer`,
222 # the concatenation is done at it would be in a `Rope`.
227 redef var chars
: Sequence[Char] is lazy
do return new RopeBufferChars(self)
229 redef var bytes
: Sequence[Byte] is lazy
do return new RopeBufferBytes(self)
231 # The final string being built on the fly
232 private var str
: String = ""
234 # Current concatenation buffer
235 private var ns
: NativeString is noinit
237 # Next available (e.g. unset) character in the `Buffer`
240 # Keeps track of the buffer's currently dumped part
242 # This might happen if for instance, a String was being
243 # built by concatenating small parts of string and suddenly
244 # a long string (length > maxlen) is appended.
245 private var dumped
: Int is noinit
247 # Length of the complete rope in chars (0)
258 # Length of the complete rope in bytes
259 redef var bytelen
= 0
261 # Length of the mutable part (in bytes)
263 # Is also used as base to compute the size of the next
264 # mutable native string (`ns`)
265 private var buf_size
: Int is noinit
267 redef fun substrings
do return new RopeBufSubstringIterator(self)
269 # Builds an empty `RopeBuffer`
271 ns
= new NativeString(maxlen
)
276 # Builds a new `RopeBuffer` with `str` in it.
277 init from
(str
: String) do
279 ns
= new NativeString(maxlen
)
285 # Resets the informations of the `Buffer`
287 # This is called when doing in-place modifications
288 # on a previously to_s'd `RopeBuffer`
290 var nns
= new NativeString(buf_size
)
291 var blen
= rpos
- dumped
292 ns
.copy_to
(nns
, blen
, dumped
, 0)
300 if i
< str
.length
then
303 var index
= ns
.char_to_byte_index_cached
(i
- str
.length
, 0, dumped
)
304 return ns
.char_at
(index
)
308 redef fun []=(i
, c
) do
309 assert i
>= 0 and i
<= length
310 if i
== length
then add c
311 if i
< str
.length
then
312 bytelen
+= c
.u8char_len
- str
[i
].u8char_len
314 var l
= s
.substring
(0, i
)
315 var r
= s
.substring_from
(i
+ 1)
318 var reali
= i
- str
.length
319 var index
= ns
.char_to_byte_index_cached
(reali
, 0, dumped
)
320 var st_nxt
= ns
.char_to_byte_index_cached
(reali
+ 1, reali
, index
)
321 var loc_c
= ns
.char_at
(index
)
322 if loc_c
.u8char_len
!= c
.u8char_len
then
323 var delta
= c
.u8char_len
- loc_c
.u8char_len
324 var remsp
= buf_size
- rpos
325 if remsp
< delta
then
327 var nns
= new NativeString(buf_size
)
328 ns
.copy_to
(nns
, index
- dumped
, dumped
, 0)
329 ns
.copy_to
(nns
, rpos
- index
- loc_c
.u8char_len
, index
+ loc_c
.u8char_len
, index
- dumped
+ delta
)
331 index
= index
- dumped
333 ns
.copy_to
(ns
, rpos
- st_nxt
, st_nxt
, st_nxt
+ delta
)
338 ns
.set_char_at
(index
, c
)
342 redef fun empty
do return new RopeBuffer
350 ns
= new NativeString(buf_size
)
355 redef fun substring
(from
, count
) do
356 var strlen
= str
.length
360 if count
< 0 then count
= 0
364 if count
> length
then count
= length
- from
366 if count
== 0 then return empty
368 if from
< strlen
then
369 var subpos
= strlen
- from
370 if count
<= subpos
then
371 return new RopeBuffer.from
(str
.substring
(from
, count
))
373 var l
= str
.substring_from
(from
)
374 var rem
= count
- subpos
375 var nns
= new NativeString(rem
)
376 ns
.copy_to
(nns
, rem
, dumped
, 0)
377 return new RopeBuffer.from
(l
+ nns
.to_s_with_length
(rem
))
380 var nns
= new NativeString(count
)
381 ns
.copy_to
(nns
, count
, dumped
, 0)
382 return new RopeBuffer.from
(nns
.to_s_with_length
(count
))
386 redef fun append
(s
) do
388 if slen
>= maxlen
then
393 if s
isa FlatText then
395 var from
= if s
isa FlatString then s
.first_byte
else 0
396 var remsp
= buf_size
- rpos
397 if slen
<= remsp
then
398 oits
.copy_to
(ns
, slen
, from
, rpos
)
402 var brk
= oits
.find_beginning_of_char_at
(from
+ remsp
)
403 oits
.copy_to
(ns
, brk
, from
, rpos
)
406 oits
.copy_to
(ns
, slen
- remsp
, brk
, 0)
409 for i
in s
.substrings
do append i
415 if rp
>= buf_size
then
419 # TODO: Fix when supporting UTF-8
420 ns
[rp
] = c
.ascii
.to_b
426 private fun add_byte
(b
: Byte) do
428 if rp
>= buf_size
then
438 # Converts the Buffer to a FlatString, appends it to
439 # the final String and re-allocates a new larger Buffer.
440 private fun dump_buffer
do
442 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
446 ns
= new NativeString(bs
)
452 # Similar to dump_buffer, but does not reallocate a new NativeString
453 private fun persist_buffer
do
454 if rpos
== dumped
then return
455 var nstr
= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
462 new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1).output
465 # Enlarge is useless here since the `Buffer`
466 # part is automatically dumped when necessary.
468 # Also, since the buffer can not be overused by a
469 # single string, there is no need for manual
472 # "You have no power here !"
473 redef fun enlarge
(i
) do end
481 # Flush the buffer in order to only have to reverse `str`.
482 if rpos
> 0 and dumped
!= rpos
then
483 str
+= new FlatString.with_infos
(ns
, rpos
- dumped
, dumped
, rpos
- 1)
490 if written
then reset
496 if written
then reset
502 redef class FlatString
504 redef fun insert_at
(s
, pos
) do
505 var l
= substring
(0, pos
)
506 var r
= substring_from
(pos
)
514 if slen
== 0 then return self
515 if mlen
== 0 then return s
516 var nlen
= slen
+ mlen
517 if s
isa FlatString then
518 if nlen
> maxlen
then return new Concat(self, s
)
520 var sifrom
= s
.first_byte
521 var mifrom
= first_byte
523 var ns
= new NativeString(nlen
+ 1)
524 mits
.copy_to
(ns
, mlen
, mifrom
, 0)
525 sits
.copy_to
(ns
, slen
, sifrom
, mlen
)
526 return ns
.to_s_with_length
(nlen
)
527 else if s
isa Concat then
529 var sllen
= sl
.bytelen
530 if sllen
+ mlen
> maxlen
then return new Concat(self, s
)
531 return new Concat(self + sl
, s
.right
)
538 # A simple linked list for use with iterators
539 private class RopeCharIteratorPiece
540 # The encapsulated node of the `Rope`
542 # Was its left child (if any) visited ?
544 # Was its right child (if any) visited ?
546 # The previous node in the list.
547 var prev
: nullable RopeCharIteratorPiece
550 # A reverse iterator capable of working with `Rope` objects
551 private class RopeByteReverseIterator
552 super IndexedIterator[Byte]
554 # Current NativeString
556 # Current position in NativeString
558 # Position in the Rope (0-indexed)
560 # Iterator on the substrings, does the Postfix part of
561 # the Rope traversal.
562 var subs
: IndexedIterator[FlatString]
564 init(root
: Concat) is old_style_init
do
565 pos
= root
.bytelen
- 1
566 subs
= new ReverseRopeSubstrings(root
)
572 init from
(root
: Concat, pos
: Int) do
574 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
577 pns
= pos
- subs
.index
580 redef fun index
do return pos
582 redef fun is_ok
do return pos
>= 0
584 redef fun item
do return ns
[pns
]
589 if pns
>= 0 then return
590 if not subs
.is_ok
then return
592 if not subs
.is_ok
then return
599 # Forward iterator on the bytes of a `Rope`
600 private class RopeByteIterator
601 super IndexedIterator[Byte]
603 # Position in current `String`
605 # Current `String` being iterated on
607 # Substrings of the Rope
608 var subs
: IndexedIterator[FlatString]
609 # Maximum position to iterate on (e.g. Rope.length)
611 # Position (char) in the Rope (0-indexed)
614 init(root
: Concat) is old_style_init
do
615 subs
= new RopeSubstrings(root
)
618 max
= root
.length
- 1
622 init from
(root
: Concat, pos
: Int) do
623 subs
= new RopeSubstrings.from
(root
, pos
)
624 pns
= pos
- subs
.index
627 max
= root
.length
- 1
630 redef fun item
do return ns
[pns
]
632 redef fun is_ok
do return pos
<= max
634 redef fun index
do return pos
639 if pns
< subs
.item
.bytelen
then return
640 if not subs
.is_ok
then return
642 if not subs
.is_ok
then return
649 # A reverse iterator capable of working with `Rope` objects
650 private class RopeCharReverseIterator
651 super IndexedIterator[Char]
653 # Current NativeString
655 # Current position in NativeString
657 # Position in the Rope (0-indexed)
659 # Iterator on the substrings, does the Postfix part of
660 # the Rope traversal.
661 var subs
: IndexedIterator[String]
663 init(root
: Concat) is old_style_init
do
664 pos
= root
.length
- 1
665 subs
= new ReverseRopeSubstrings(root
)
670 init from
(root
: Concat, pos
: Int) do
672 subs
= new ReverseRopeSubstrings.from
(root
, pos
)
674 pns
= pos
- subs
.index
677 redef fun index
do return pos
679 redef fun is_ok
do return pos
>= 0
681 redef fun item
do return ns
[pns
]
686 if pns
>= 0 then return
687 if not subs
.is_ok
then return
689 if not subs
.is_ok
then return
695 # Forward iterator on the chars of a `Rope`
696 private class RopeCharIterator
697 super IndexedIterator[Char]
699 # Position in current `String`
701 # Current `String` being iterated on
703 # Substrings of the Rope
704 var subs
: IndexedIterator[String]
705 # Maximum position to iterate on (e.g. Rope.length)
707 # Position (char) in the Rope (0-indexed)
710 init(root
: Concat) is old_style_init
do
711 subs
= new RopeSubstrings(root
)
714 max
= root
.length
- 1
718 init from
(root
: Concat, pos
: Int) do
719 subs
= new RopeSubstrings.from
(root
, pos
)
720 pns
= pos
- subs
.index
723 max
= root
.length
- 1
726 redef fun item
do return str
[pns
]
728 redef fun is_ok
do return pos
<= max
730 redef fun index
do return pos
735 if pns
< subs
.item
.length
then return
736 if not subs
.is_ok
then return
738 if not subs
.is_ok
then return
744 # Substrings of a Rope (i.e. Reverse postfix iterator on leaves)
745 private class ReverseRopeSubstrings
746 super IndexedIterator[FlatString]
749 var iter
: RopeCharIteratorPiece is noinit
751 var pos
: Int is noinit
754 var str
: FlatString is noinit
756 init(root
: Concat) is old_style_init
do
757 var r
= new RopeCharIteratorPiece(root
, false, true, null)
758 pos
= root
.length
- 1
759 var lnod
: String = root
761 if lnod
isa Concat then
763 r
= new RopeCharIteratorPiece(lnod
, false, true, r
)
765 str
= lnod
.as(FlatString)
772 init from
(root
: Concat, pos
: Int) do
773 var r
= new RopeCharIteratorPiece(root
, false, true, null)
774 var rnod
: String = root
777 if rnod
isa Concat then
778 if off
>= rnod
.left
.length
then
779 off
-= rnod
.left
.length
781 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
785 r
= new RopeCharIteratorPiece(rnod
, false, true, r
)
788 str
= rnod
.as(FlatString)
797 redef fun item
do return str
799 redef fun index
do return pos
801 redef fun is_ok
do return pos
>= 0
804 if pos
< 0 then return
806 var currit
= curr
.node
807 while curr
!= null do
809 if not currit
isa Concat then
810 str
= currit
.as(FlatString)
815 if not curr
.rdone
then
817 curr
= new RopeCharIteratorPiece(currit
.right
, false, false, curr
)
820 if not curr
.ldone
then
822 curr
= new RopeCharIteratorPiece(currit
.left
, false, false, curr
)
831 private class RopeBufSubstringIterator
832 super Iterator[FlatText]
834 # Iterator on the substrings of the building string
835 var iter
: Iterator[FlatText]
836 # Makes a String out of the buffered part of the Ropebuffer
837 var nsstr
: FlatString
838 # Did we attain the buffered part ?
839 var nsstr_done
= false
841 init(str
: RopeBuffer) is old_style_init
do
842 iter
= str
.str
.substrings
843 nsstr
= new FlatString.with_infos
(str
.ns
, str
.rpos
- str
.dumped
, str
.dumped
, str
.rpos
- 1)
844 if str
.length
== 0 then nsstr_done
= true
847 redef fun is_ok
do return iter
.is_ok
or not nsstr_done
851 if iter
.is_ok
then return iter
.item
864 # Substrings of a Rope (i.e. Postfix iterator on leaves)
865 private class RopeSubstrings
866 super IndexedIterator[FlatString]
869 var iter
: RopeCharIteratorPiece is noinit
871 var pos
: Int is noinit
872 # Maximum position in `Rope` (i.e. length - 1)
873 var max
: Int is noinit
876 var str
: FlatString is noinit
878 init(root
: Concat) is old_style_init
do
879 var r
= new RopeCharIteratorPiece(root
, true, false, null)
881 max
= root
.length
- 1
882 var rnod
: String = root
884 if rnod
isa Concat then
886 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
888 str
= rnod
.as(FlatString)
896 init from
(root
: Concat, pos
: Int) do
897 var r
= new RopeCharIteratorPiece(root
, true, false, null)
898 max
= root
.length
- 1
899 var rnod
: String = root
902 if rnod
isa Concat then
903 if off
>= rnod
.left
.length
then
905 off
-= rnod
.left
.length
907 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
910 r
= new RopeCharIteratorPiece(rnod
, true, false, r
)
913 str
= rnod
.as(FlatString)
922 redef fun item
do return str
924 redef fun is_ok
do return pos
<= max
926 redef fun index
do return pos
930 if pos
> max
then return
934 if not rnod
isa Concat then
937 str
= rnod
.as(FlatString)
938 iter
= it
.as(not null)
944 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
945 else if not it
.rdone
then
948 it
= new RopeCharIteratorPiece(rnod
, false, false, it
)
958 # Implementation of a `StringCharView` for `Concat` objects
959 private class RopeChars
962 redef type SELFTYPE: Concat
968 redef fun iterator_from
(i
) do return new RopeCharIterator.from
(target
, i
)
970 redef fun reverse_iterator_from
(i
) do return new RopeCharReverseIterator.from
(target
, i
)
974 # Implementation of a `StringCharView` for `Concat` objects
975 private class RopeBytes
978 redef type SELFTYPE: Concat
981 var nod
: String = target
983 if nod
isa FlatString then return nod
.items
[i
]
984 if not nod
isa Concat then abort
985 if nod
.left
.bytelen
>= i
then
993 redef fun iterator_from
(i
) do return new RopeByteIterator.from
(target
, i
)
995 redef fun reverse_iterator_from
(i
) do return new RopeByteReverseIterator.from
(target
, i
)
999 # An Iterator over a RopeBuffer.
1000 class RopeBufferCharIterator
1001 super IndexedIterator[Char]
1004 var sit
: IndexedIterator[Char]
1006 redef fun index
do return sit
.index
1008 # Init the iterator from a RopeBuffer.
1009 init(t
: RopeBuffer) is old_style_init
do
1011 sit
= t
.str
.chars
.iterator
1014 # Init the iterator from a RopeBuffer starting from `pos`.
1015 init from
(t
: RopeBuffer, pos
: Int) do
1017 sit
= t
.str
.chars
.iterator_from
(pos
)
1020 redef fun is_ok
do return sit
.is_ok
1027 redef fun next
do sit
.next
1030 # Reverse iterator over a RopeBuffer.
1031 class RopeBufferCharReverseIterator
1032 super IndexedIterator[Char]
1035 var sit
: IndexedIterator[Char]
1037 redef fun index
do return sit
.index
1039 # Init the iterator from a RopeBuffer.
1040 init(tgt
: RopeBuffer) is old_style_init
do
1042 sit
= tgt
.str
.chars
.reverse_iterator
1045 # Init the iterator from a RopeBuffer starting from `pos`.
1046 init from
(tgt
: RopeBuffer, pos
: Int) do
1048 sit
= tgt
.str
.chars
.reverse_iterator_from
(pos
)
1051 redef fun is_ok
do return sit
.is_ok
1058 redef fun next
do sit
.next
1061 # View on the chars of a `RopeBuffer`
1062 class RopeBufferChars
1063 super BufferCharView
1065 redef type SELFTYPE: RopeBuffer
1067 redef fun [](i
) do return target
[i
]
1069 redef fun []=(i
,c
) do target
[i
] = c
1071 redef fun add
(c
) do target
.add c
1073 redef fun push
(c
) do target
.add c
1075 redef fun iterator_from
(i
) do return new RopeBufferCharIterator.from
(target
, i
)
1077 redef fun reverse_iterator_from
(i
) do return new RopeBufferCharReverseIterator.from
(target
, i
)
1080 # An Iterator over a RopeBuffer.
1081 class RopeBufferByteIterator
1082 super IndexedIterator[Byte]
1085 var sit
: IndexedIterator[Byte]
1087 # Native string iterated over.
1088 var ns
: NativeString
1090 # Current position in `ns`.
1093 # Maximum position iterable.
1098 # Init the iterator from a RopeBuffer.
1099 init(t
: RopeBuffer) is old_style_init
do
1102 sit
= t
.str
.bytes
.iterator
1107 # Init the iterator from a RopeBuffer starting from `pos`.
1108 init from
(t
: RopeBuffer, pos
: Int) do
1111 sit
= t
.str
.bytes
.iterator_from
(pos
)
1112 pns
= pos
- t
.str
.length
1116 redef fun is_ok
do return index
< maxpos
1119 if sit
.is_ok
then return sit
.item
1133 # Reverse iterator over a RopeBuffer.
1134 class RopeBufferByteReverseIterator
1135 super IndexedIterator[Byte]
1138 var sit
: IndexedIterator[Byte]
1140 # Native string iterated over.
1141 var ns
: NativeString
1143 # Current position in `ns`.
1148 # Init the iterator from a RopeBuffer.
1149 init(tgt
: RopeBuffer) is old_style_init
do
1150 sit
= tgt
.str
.bytes
.reverse_iterator
1152 index
= tgt
.bytelen
- 1
1156 # Init the iterator from a RopeBuffer starting from `pos`.
1157 init from
(tgt
: RopeBuffer, pos
: Int) do
1158 sit
= tgt
.str
.bytes
.reverse_iterator_from
(pos
- (tgt
.rpos
- tgt
.dumped
))
1159 pns
= pos
- tgt
.str
.bytelen
+ tgt
.rpos
1164 redef fun is_ok
do return index
>= 0
1167 if pns
>= 0 then return ns
[pns
]
1181 # View on the chars of a `RopeBuffer`
1182 class RopeBufferBytes
1183 super BufferByteView
1185 redef type SELFTYPE: RopeBuffer
1188 if i
< target
.str
.bytelen
then
1189 return target
.str
.bytes
[i
]
1191 return target
.ns
[i
- target
.str
.bytelen
]
1195 redef fun []=(i
,c
) do
1196 if i
== target
.length
then target
.add_byte c
1197 if i
< target
.str
.length
then
1198 # FIXME: Will need to be optimized and rewritten with Unicode
1200 var l
= s
.substring
(0, i
)
1201 var r
= s
.substring_from
(i
+ 1)
1202 target
.str
= l
+ c
.to_i
.ascii
.to_s
+ r
1204 target
.ns
[i
- target
.str
.length
] = c
1208 redef fun add
(c
) do target
.add_byte c
1210 redef fun push
(c
) do target
.add_byte c
1212 redef fun iterator_from
(i
) do return new RopeBufferByteIterator.from
(target
, i
)
1214 redef fun reverse_iterator_from
(i
) do return new RopeBufferByteReverseIterator.from
(target
, i
)