lib/core/text: Replaced most polymorph accesses to Text attributes by direct accesses
[nit.git] / lib / core / text / ropes.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
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
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
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.
14
15 # Tree-based representation of a String.
16 #
17 # Ropes are a data structure introduced in a 1995 paper from
18 # Hans J. Boehm, Russ Atkinson and Michael Plass.
19 #
20 # See:
21 #
22 # > Ropes: an Alternative to Strings,
23 # > *Software - Practice and Experience*,
24 # > Vol. 25(12), 1315-1330 (December 1995).
25 #
26 # The implementation developed here provides an automatic change
27 # of data structure depending on the length of the leaves.
28 #
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).
33 #
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.
36 #
37 # Example :
38 #
39 # ~~~raw
40 # Concat
41 # / \
42 # Concat Concat
43 # / \ / \
44 # "My" " Name" " is" " Simon."
45 # ~~~
46 #
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` !).
50 module ropes
51
52 intrude import flat
53
54 # Maxlen is the maximum length of a Leaf node
55 #
56 # When concatenating two leaves, if `new_length` > `maxlen`,
57 # A `Concat` node is created instead
58 #
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
62
63 # String using a tree-based representation with leaves as `FlatStrings`
64 private abstract class Rope
65 super Text
66 end
67
68 # Node that represents a concatenation between two `String`
69 private class Concat
70 super Rope
71 super String
72
73 redef var chars is lazy do return new RopeChars(self)
74
75 redef var bytes is lazy do return new RopeBytes(self)
76
77 redef var length is noinit
78
79 redef var bytelen is noinit
80
81 redef fun substrings do return new RopeSubstrings(self)
82
83 redef fun empty do return ""
84
85 # Cache for the latest accessed FlatString in `self`
86 var flat_cache: String = ""
87
88 # Position of the beginning of `flat_cache` in `self`
89 var flat_last_pos_start: Int = -1
90
91 redef var to_cstring is lazy do
92 var len = _bytelen
93 var ns = new NativeString(len + 1)
94 ns[len] = 0u8
95 var off = 0
96 for i in substrings do
97 var ilen = i._bytelen
98 i.as(FlatString)._items.copy_to(ns, ilen, i.as(FlatString)._first_byte, off)
99 off += ilen
100 end
101 return ns
102 end
103
104 # Left child of the node
105 var left: String
106 # Right child of the node
107 var right: String
108
109 init do
110 var l = _left
111 var r = _right
112 length = l.length + r.length
113 _bytelen = l.bytelen + r.bytelen
114 end
115
116 redef fun output do
117 _left.output
118 _right.output
119 end
120
121 redef fun iterator do return new RopeCharIterator(self)
122
123 redef fun *(i) do
124 var x: String = self
125 for j in [1 .. i[ do x += self
126 return x
127 end
128
129 redef fun [](i) do
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]
133 end
134 var s: String = self
135 var st = i
136 loop
137 if s isa FlatString then break
138 s = s.as(Concat)
139 var lft = s._left
140 var llen = lft.length
141 if i >= llen then
142 s = s._right
143 i -= llen
144 else
145 s = lft
146 end
147 end
148 flat_last_pos_start = st - i
149 flat_cache = s
150 return s[i]
151 end
152
153 redef fun substring(from, len) do
154 var lft = _left
155 var llen = lft.length
156 if from < llen then
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)
160 else
161 return _right.substring(from - llen, len)
162 end
163 end
164
165 redef fun reversed do return new Concat(_right.reversed, _left.reversed)
166
167 redef fun insert_at(s, pos) do
168 var lft = _left
169 if pos > lft.length then
170 return lft + _right.insert_at(s, pos - lft.length)
171 end
172 return lft.insert_at(s, pos) + _right
173 end
174
175 redef fun to_upper do return new Concat(_left.to_upper, _right.to_upper)
176
177 redef fun to_lower do return new Concat(_left.to_lower, _right.to_lower)
178
179 redef fun +(o) do
180 var s = o.to_s
181 var slen = s.bytelen
182 if s isa Concat then
183 return new Concat(self, s)
184 else
185 var r = _right
186 var rlen = r.bytelen
187 if rlen + slen > maxlen then return new Concat(self, s)
188 return new Concat(_left, r + s)
189 end
190 end
191
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
196 while n > 0 do
197 var it = subs.item
198 if n > it.length then
199 var cplen = it.length - st
200 it._items.copy_to(dest, cplen, st, off)
201 off += cplen
202 n -= cplen
203 else
204 it._items.copy_to(dest, n, st, off)
205 n = 0
206 end
207 subs.next
208 st = 0
209 end
210 end
211
212 # Returns a balanced version of `self`
213 fun balance: String do
214 var children = new Array[String]
215 var rnod: String
216 var iter: nullable RopeCharIteratorPiece = new RopeCharIteratorPiece(self, false, false, null)
217 loop
218 if iter == null then break
219 rnod = iter.node
220 if not rnod isa Concat then
221 children.push rnod
222 iter = iter.prev
223 continue
224 end
225 if not iter.ldone then
226 iter.ldone = true
227 iter = new RopeCharIteratorPiece(rnod._left, false, false, iter)
228 else if not iter.rdone then
229 iter.rdone = true
230 iter = new RopeCharIteratorPiece(rnod._right, false, false, iter)
231 else
232 iter = iter.prev
233 end
234
235 end
236 return recurse_balance(children, children.length)
237 end
238
239 fun recurse_balance(nodes: Array[String], len: Int): String do
240 var finpos = 0
241 var stpos = 0
242 while stpos < len do
243 if len - stpos > 1 then
244 nodes[finpos] = new Concat(nodes[stpos], nodes[stpos + 1])
245 stpos += 2
246 else
247 nodes[finpos] = nodes[stpos]
248 stpos += 1
249 end
250 finpos += 1
251 end
252 if finpos == 1 then return nodes[0]
253 return recurse_balance(nodes, finpos)
254 end
255 end
256
257 # Mutable `Rope`, optimized for concatenation operations
258 #
259 # A `RopeBuffer` is an efficient way of building a `String` when
260 # concatenating small strings.
261 #
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.
265 #
266 # Every concatenation operation is done by copying a string to
267 # the mutable part and flushing it when full.
268 #
269 # However, when a long string is appended to the `Buffer`,
270 # the concatenation is done at it would be in a `Rope`.
271 class RopeBuffer
272 super Rope
273 super Buffer
274
275 redef var chars: Sequence[Char] is lazy do return new RopeBufferChars(self)
276
277 redef var bytes is lazy do return new RopeBufferBytes(self)
278
279 # The final string being built on the fly
280 private var str: String = ""
281
282 # Current concatenation buffer
283 private var ns: NativeString is noinit
284
285 # Next available (e.g. unset) character in the `Buffer`
286 private var rpos = 0
287
288 # Length (in chars) of the buffered part
289 private var nslen = 0
290
291 # Keeps track of the buffer's currently dumped part
292 #
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
297
298 # Length of the complete rope in chars (0)
299 redef fun length do
300 var st = dumped
301 var len = str.length
302 while st < rpos do
303 st += ns[st].u8len
304 len += 1
305 end
306 return len
307 end
308
309 # Length of the complete rope in bytes
310 redef var bytelen = 0
311
312 # Length of the mutable part (in bytes)
313 #
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
317
318 redef fun substrings do return new RopeBufSubstringIterator(self)
319
320 # Builds an empty `RopeBuffer`
321 init do
322 ns = new NativeString(maxlen)
323 buf_size = maxlen
324 dumped = 0
325 end
326
327 # Builds a new `RopeBuffer` with `str` in it.
328 init from(str: String) do
329 self.str = str
330 ns = new NativeString(maxlen)
331 buf_size = maxlen
332 _bytelen = str.length
333 dumped = 0
334 end
335
336 # Resets the informations of the `Buffer`
337 #
338 # This is called when doing in-place modifications
339 # on a previously to_s'd `RopeBuffer`
340 private fun reset do
341 var nns = new NativeString(buf_size)
342 var blen = rpos - dumped
343 ns.copy_to(nns, blen, dumped, 0)
344 ns = nns
345 dumped = 0
346 rpos = blen
347 written = false
348 end
349
350 redef fun [](i) do
351 if i < str.length then
352 return str[i]
353 else
354 var index = ns.char_to_byte_index_cached(i - str.length, 0, dumped)
355 return ns.char_at(index)
356 end
357 end
358
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
364 var s = str
365 var l = s.substring(0, i)
366 var r = s.substring_from(i + 1)
367 str = l + c.to_s + r
368 else
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
377 buf_size *= 2
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)
381 ns = nns
382 index = index - dumped
383 else
384 ns.copy_to(ns, rpos - st_nxt, st_nxt, st_nxt + delta)
385 end
386 _bytelen += delta
387 rpos += delta
388 end
389 ns.set_char_at(index, c)
390 end
391 end
392
393 redef fun empty do return new RopeBuffer
394
395 redef fun clear do
396 str = ""
397 _bytelen = 0
398 rpos = 0
399 dumped = 0
400 if written then
401 ns = new NativeString(buf_size)
402 written = false
403 end
404 end
405
406 redef fun substring(from, count) do
407 var strlen = str.length
408
409 if from < 0 then
410 count += from
411 if count < 0 then count = 0
412 from = 0
413 end
414
415 if count > length then count = length - from
416
417 if count == 0 then return empty
418
419 if from < strlen then
420 var subpos = strlen - from
421 if count <= subpos then
422 return new RopeBuffer.from(str.substring(from, count))
423 else
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))
429 end
430 else
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))
434 end
435 end
436
437 redef fun append(s) do
438 var slen = s.bytelen
439 if slen >= maxlen then
440 persist_buffer
441 str += s.to_s
442 return
443 end
444 if s isa FlatText then
445 var oits = s._items
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)
450 rpos += slen
451 return
452 end
453 var brk = oits.find_beginning_of_char_at(from + remsp)
454 oits.copy_to(ns, brk, from, rpos)
455 rpos += brk
456 dump_buffer
457 oits.copy_to(ns, slen - remsp, brk, 0)
458 rpos = slen - remsp
459 else
460 for i in s.substrings do append i
461 end
462 end
463
464 redef fun add(c) do
465 var rp = rpos
466 if rp >= buf_size then
467 dump_buffer
468 rp = 0
469 end
470 # TODO: Fix when supporting UTF-8
471 ns[rp] = c.ascii.to_b
472 rp += 1
473 _bytelen += 1
474 rpos = rp
475 end
476
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
480 written = false
481 var nstr = new FlatString.with_infos(ns, rpos - dumped, dumped, rpos - 1)
482 str += nstr
483 var bs = buf_size
484 bs = bs * 2
485 ns = new NativeString(bs)
486 buf_size = bs
487 dumped = 0
488 rpos = 0
489 end
490
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)
495 str += nstr
496 dumped = rpos
497 end
498
499 redef fun output do
500 str.output
501 new FlatString.with_infos(ns, rpos - dumped, dumped, rpos - 1).output
502 end
503
504 # Enlarge is useless here since the `Buffer`
505 # part is automatically dumped when necessary.
506 #
507 # Also, since the buffer can not be overused by a
508 # single string, there is no need for manual
509 # resizing.
510 #
511 # "You have no power here !"
512 redef fun enlarge(i) do end
513
514 redef fun to_s do
515 persist_buffer
516 written = true
517 return str
518 end
519
520 redef fun reverse do
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)
524 dumped = rpos
525 end
526 str = str.reversed
527 end
528
529 redef fun upper do
530 if written then reset
531 persist_buffer
532 str = str.to_upper
533 end
534
535 redef fun lower do
536 if written then reset
537 persist_buffer
538 str = str.to_lower
539 end
540 end
541
542 redef class FlatString
543
544 redef fun insert_at(s, pos) do
545 var l = substring(0, pos)
546 var r = substring_from(pos)
547 return l + s + r
548 end
549
550 redef fun +(o) do
551 var s = o.to_s
552 var slen = s.bytelen
553 var mlen = _bytelen
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)
559 var mits = _items
560 var sifrom = s._first_byte
561 var mifrom = _first_byte
562 var sits = s._items
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
568 var sl = s._left
569 var sllen = sl.bytelen
570 if sllen + mlen > maxlen then return new Concat(self, s)
571 return new Concat(self + sl, s._right)
572 else
573 abort
574 end
575 end
576 end
577
578 # A simple linked list for use with iterators
579 private class RopeCharIteratorPiece
580 # The encapsulated node of the `Rope`
581 var node: String
582 # Was its _left child (if any) visited ?
583 var ldone: Bool
584 # Was its _right child (if any) visited ?
585 var rdone: Bool
586 # The previous node in the list.
587 var prev: nullable RopeCharIteratorPiece
588 end
589
590 # A reverse iterator capable of working with `Rope` objects
591 private class RopeByteReverseIterator
592 super IndexedIterator[Byte]
593
594 # Current NativeString
595 var ns: NativeString
596 # Current position in NativeString
597 var pns: Int
598 # Position in the Rope (0-indexed)
599 var pos: Int
600 # Iterator on the substrings, does the Postfix part of
601 # the Rope traversal.
602 var subs: IndexedIterator[FlatString]
603
604 init(root: Concat) is old_style_init do
605 pos = root._bytelen - 1
606 subs = new ReverseRopeSubstrings(root)
607 var s = subs.item
608 ns = s._items
609 pns = s._last_byte
610 end
611
612 init from(root: Concat, pos: Int) do
613 self.pos = pos
614 subs = new ReverseRopeSubstrings.from(root, pos)
615 var s = subs.item
616 ns = s._items
617 pns = pos - subs.index
618 end
619
620 redef fun index do return pos
621
622 redef fun is_ok do return pos >= 0
623
624 redef fun item do return ns[pns]
625
626 redef fun next do
627 pns -= 1
628 pos -= 1
629 if pns >= 0 then return
630 if not subs.is_ok then return
631 subs.next
632 if not subs.is_ok then return
633 var s = subs.item
634 ns = s._items
635 pns = s._last_byte
636 end
637 end
638
639 # Forward iterator on the bytes of a `Rope`
640 private class RopeByteIterator
641 super IndexedIterator[Byte]
642
643 # Position in current `String`
644 var pns: Int
645 # Current `String` being iterated on
646 var ns: NativeString
647 # Substrings of the Rope
648 var subs: IndexedIterator[FlatString]
649 # Maximum position to iterate on (e.g. Rope.length)
650 var max: Int
651 # Position (char) in the Rope (0-indexed)
652 var pos: Int
653
654 init(root: Concat) is old_style_init do
655 subs = new RopeSubstrings(root)
656 pns = 0
657 ns = subs.item._items
658 max = root.length - 1
659 pos = 0
660 end
661
662 init from(root: Concat, pos: Int) do
663 subs = new RopeSubstrings.from(root, pos)
664 pns = pos - subs.index
665 self.pos = pos
666 ns = subs.item._items
667 max = root.length - 1
668 end
669
670 redef fun item do return ns[pns]
671
672 redef fun is_ok do return pos <= max
673
674 redef fun index do return pos
675
676 redef fun next do
677 pns += 1
678 pos += 1
679 if pns < subs.item._bytelen then return
680 if not subs.is_ok then return
681 subs.next
682 if not subs.is_ok then return
683 ns = subs.item._items
684 pns = 0
685 end
686 end
687
688
689 # A reverse iterator capable of working with `Rope` objects
690 private class RopeCharReverseIterator
691 super IndexedIterator[Char]
692
693 # Current NativeString
694 var ns: String
695 # Current position in NativeString
696 var pns: Int
697 # Position in the Rope (0-indexed)
698 var pos: Int
699 # Iterator on the substrings, does the Postfix part of
700 # the Rope traversal.
701 var subs: IndexedIterator[String]
702
703 init(root: Concat) is old_style_init do
704 pos = root.length - 1
705 subs = new ReverseRopeSubstrings(root)
706 ns = subs.item
707 pns = ns.length - 1
708 end
709
710 init from(root: Concat, pos: Int) do
711 self.pos = pos
712 subs = new ReverseRopeSubstrings.from(root, pos)
713 ns = subs.item
714 pns = pos - subs.index
715 end
716
717 redef fun index do return pos
718
719 redef fun is_ok do return pos >= 0
720
721 redef fun item do return ns[pns]
722
723 redef fun next do
724 pns -= 1
725 pos -= 1
726 if pns >= 0 then return
727 if not subs.is_ok then return
728 subs.next
729 if not subs.is_ok then return
730 ns = subs.item
731 pns = ns.length - 1
732 end
733 end
734
735 # Forward iterator on the chars of a `Rope`
736 private class RopeCharIterator
737 super IndexedIterator[Char]
738
739 # Position in current `String`
740 var pns: Int
741 # Current `String` being iterated on
742 var str: String
743 # Substrings of the Rope
744 var subs: IndexedIterator[String]
745 # Maximum position to iterate on (e.g. Rope.length)
746 var max: Int
747 # Position (char) in the Rope (0-indexed)
748 var pos: Int
749
750 init(root: Concat) is old_style_init do
751 subs = new RopeSubstrings(root)
752 pns = 0
753 str = subs.item
754 max = root.length - 1
755 pos = 0
756 end
757
758 init from(root: Concat, pos: Int) do
759 subs = new RopeSubstrings.from(root, pos)
760 pns = pos - subs.index
761 self.pos = pos
762 str = subs.item
763 max = root.length - 1
764 end
765
766 redef fun item do return str[pns]
767
768 redef fun is_ok do return pos <= max
769
770 redef fun index do return pos
771
772 redef fun next do
773 pns += 1
774 pos += 1
775 if pns < subs.item.length then return
776 if not subs.is_ok then return
777 subs.next
778 if not subs.is_ok then return
779 str = subs.item
780 pns = 0
781 end
782 end
783
784 # Substrings of a Rope (i.e. Reverse postfix iterator on leaves)
785 private class ReverseRopeSubstrings
786 super IndexedIterator[FlatString]
787
788 # Visit Stack
789 var iter: RopeCharIteratorPiece is noinit
790 # Position in `Rope`
791 var pos: Int is noinit
792
793 # Current leaf
794 var str: FlatString is noinit
795
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
800 loop
801 if lnod isa Concat then
802 lnod = lnod._right
803 r = new RopeCharIteratorPiece(lnod, false, true, r)
804 else
805 str = lnod.as(FlatString)
806 iter = r
807 break
808 end
809 end
810 end
811
812 init from(root: Concat, pos: Int) do
813 var r = new RopeCharIteratorPiece(root, false, true, null)
814 var rnod: String = root
815 var off = pos
816 loop
817 if rnod isa Concat then
818 if off >= rnod._left.length then
819 off -= rnod._left.length
820 rnod = rnod._right
821 r = new RopeCharIteratorPiece(rnod, false, true, r)
822 else
823 r.ldone = true
824 rnod = rnod._left
825 r = new RopeCharIteratorPiece(rnod, false, true, r)
826 end
827 else
828 str = rnod.as(FlatString)
829 r.ldone = true
830 iter = r
831 self.pos = pos - off
832 break
833 end
834 end
835 end
836
837 redef fun item do return str
838
839 redef fun index do return pos
840
841 redef fun is_ok do return pos >= 0
842
843 redef fun next do
844 if pos < 0 then return
845 var curr = iter.prev
846 var currit = curr.node
847 while curr != null do
848 currit = curr.node
849 if not currit isa Concat then
850 str = currit.as(FlatString)
851 pos -= str.length
852 iter = curr
853 return
854 end
855 if not curr.rdone then
856 curr.rdone = true
857 curr = new RopeCharIteratorPiece(currit._right, false, false, curr)
858 continue
859 end
860 if not curr.ldone then
861 curr.ldone = true
862 curr = new RopeCharIteratorPiece(currit._left, false, false, curr)
863 continue
864 end
865 curr = curr.prev
866 end
867 pos = -1
868 end
869 end
870
871 private class RopeBufSubstringIterator
872 super Iterator[FlatText]
873
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
880
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
885 end
886
887 redef fun is_ok do return iter.is_ok or not nsstr_done
888
889 redef fun item do
890 assert is_ok
891 if iter.is_ok then return iter.item
892 return nsstr
893 end
894
895 redef fun next do
896 if iter.is_ok then
897 iter.next
898 return
899 end
900 nsstr_done = true
901 end
902 end
903
904 # Substrings of a Rope (i.e. Postfix iterator on leaves)
905 private class RopeSubstrings
906 super IndexedIterator[FlatString]
907
908 # Visit Stack
909 var iter: RopeCharIteratorPiece is noinit
910 # Position in `Rope`
911 var pos: Int is noinit
912 # Maximum position in `Rope` (i.e. length - 1)
913 var max: Int is noinit
914
915 # Current leaf
916 var str: FlatString is noinit
917
918 init(root: Concat) is old_style_init do
919 var r = new RopeCharIteratorPiece(root, true, false, null)
920 pos = 0
921 max = root.length - 1
922 var rnod: String = root
923 loop
924 if rnod isa Concat then
925 rnod = rnod._left
926 r = new RopeCharIteratorPiece(rnod, true, false, r)
927 else
928 str = rnod.as(FlatString)
929 r.rdone = true
930 iter = r
931 break
932 end
933 end
934 end
935
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
940 var off = pos
941 loop
942 if rnod isa Concat then
943 if off >= rnod._left.length then
944 r.rdone = true
945 off -= rnod._left.length
946 rnod = rnod._right
947 r = new RopeCharIteratorPiece(rnod, true, false, r)
948 else
949 rnod = rnod._left
950 r = new RopeCharIteratorPiece(rnod, true, false, r)
951 end
952 else
953 str = rnod.as(FlatString)
954 r.rdone = true
955 iter = r
956 self.pos = pos - off
957 break
958 end
959 end
960 end
961
962 redef fun item do return str
963
964 redef fun is_ok do return pos <= max
965
966 redef fun index do return pos
967
968 redef fun next do
969 pos += str.length
970 if pos > max then return
971 var it = iter.prev
972 var rnod = it.node
973 loop
974 if not rnod isa Concat then
975 it.ldone = true
976 it.rdone = true
977 str = rnod.as(FlatString)
978 iter = it.as(not null)
979 break
980 end
981 if not it.ldone then
982 rnod = rnod._left
983 it.ldone = true
984 it = new RopeCharIteratorPiece(rnod, false, false, it)
985 else if not it.rdone then
986 it.rdone = true
987 rnod = rnod._right
988 it = new RopeCharIteratorPiece(rnod, false, false, it)
989 else
990 it = it.prev
991 rnod = it.node
992 continue
993 end
994 end
995 end
996 end
997
998 # Implementation of a `StringCharView` for `Concat` objects
999 private class RopeChars
1000 super StringCharView
1001
1002 redef type SELFTYPE: Concat
1003
1004 redef fun [](i) do
1005 return target[i]
1006 end
1007
1008 redef fun iterator_from(i) do return new RopeCharIterator.from(target, i)
1009
1010 redef fun reverse_iterator_from(i) do return new RopeCharReverseIterator.from(target, i)
1011
1012 end
1013
1014 # Implementation of a `StringCharView` for `Concat` objects
1015 private class RopeBytes
1016 super StringByteView
1017
1018 redef type SELFTYPE: Concat
1019
1020 redef fun [](i) do
1021 var nod: String = target
1022 loop
1023 if nod isa FlatString then return nod._items[i]
1024 if not nod isa Concat then abort
1025 var lft = nod._left
1026 if lft.bytelen >= i then
1027 nod = nod._right
1028 else
1029 nod = lft
1030 end
1031 end
1032 end
1033
1034 redef fun iterator_from(i) do return new RopeByteIterator.from(target, i)
1035
1036 redef fun reverse_iterator_from(i) do return new RopeByteReverseIterator.from(target, i)
1037
1038 end
1039
1040 # An Iterator over a RopeBuffer.
1041 class RopeBufferCharIterator
1042 super IndexedIterator[Char]
1043
1044 # Subiterator.
1045 var sit: IndexedIterator[Char]
1046
1047 redef fun index do return sit.index
1048
1049 # Init the iterator from a RopeBuffer.
1050 init(t: RopeBuffer) is old_style_init do
1051 t.persist_buffer
1052 sit = t.str.chars.iterator
1053 end
1054
1055 # Init the iterator from a RopeBuffer starting from `pos`.
1056 init from(t: RopeBuffer, pos: Int) do
1057 t.persist_buffer
1058 sit = t.str.chars.iterator_from(pos)
1059 end
1060
1061 redef fun is_ok do return sit.is_ok
1062
1063 redef fun item do
1064 assert is_ok
1065 return sit.item
1066 end
1067
1068 redef fun next do sit.next
1069 end
1070
1071 # Reverse iterator over a RopeBuffer.
1072 class RopeBufferCharReverseIterator
1073 super IndexedIterator[Char]
1074
1075 # Subiterator.
1076 var sit: IndexedIterator[Char]
1077
1078 redef fun index do return sit.index
1079
1080 # Init the iterator from a RopeBuffer.
1081 init(tgt: RopeBuffer) is old_style_init do
1082 tgt.persist_buffer
1083 sit = tgt.str.chars.reverse_iterator
1084 end
1085
1086 # Init the iterator from a RopeBuffer starting from `pos`.
1087 init from(tgt: RopeBuffer, pos: Int) do
1088 tgt.persist_buffer
1089 sit = tgt.str.chars.reverse_iterator_from(pos)
1090 end
1091
1092 redef fun is_ok do return sit.is_ok
1093
1094 redef fun item do
1095 assert is_ok
1096 return sit.item
1097 end
1098
1099 redef fun next do sit.next
1100 end
1101
1102 # View on the chars of a `RopeBuffer`
1103 class RopeBufferChars
1104 super BufferCharView
1105
1106 redef type SELFTYPE: RopeBuffer
1107
1108 redef fun [](i) do return target[i]
1109
1110 redef fun []=(i,c) do target[i] = c
1111
1112 redef fun add(c) do target.add c
1113
1114 redef fun push(c) do target.add c
1115
1116 redef fun iterator_from(i) do return new RopeBufferCharIterator.from(target, i)
1117
1118 redef fun reverse_iterator_from(i) do return new RopeBufferCharReverseIterator.from(target, i)
1119 end
1120
1121 # An Iterator over a RopeBuffer.
1122 class RopeBufferByteIterator
1123 super IndexedIterator[Byte]
1124
1125 # Subiterator.
1126 var sit: IndexedIterator[Byte]
1127
1128 # Native string iterated over.
1129 var ns: NativeString
1130
1131 # Current position in `ns`.
1132 var pns: Int
1133
1134 # Maximum position iterable.
1135 var maxpos: Int
1136
1137 redef var index
1138
1139 # Init the iterator from a RopeBuffer.
1140 init(t: RopeBuffer) is old_style_init do
1141 ns = t.ns
1142 maxpos = t._bytelen
1143 sit = t.str.bytes.iterator
1144 pns = t.dumped
1145 index = 0
1146 end
1147
1148 # Init the iterator from a RopeBuffer starting from `pos`.
1149 init from(t: RopeBuffer, pos: Int) do
1150 ns = t.ns
1151 maxpos = t._bytelen
1152 sit = t.str.bytes.iterator_from(pos)
1153 pns = pos - t.str.length
1154 index = pos
1155 end
1156
1157 redef fun is_ok do return index < maxpos
1158
1159 redef fun item do
1160 if sit.is_ok then return sit.item
1161 return ns[pns]
1162 end
1163
1164 redef fun next do
1165 index += 1
1166 if sit.is_ok then
1167 sit.next
1168 else
1169 pns += 1
1170 end
1171 end
1172 end
1173
1174 # Reverse iterator over a RopeBuffer.
1175 class RopeBufferByteReverseIterator
1176 super IndexedIterator[Byte]
1177
1178 # Subiterator.
1179 var sit: IndexedIterator[Byte]
1180
1181 # Native string iterated over.
1182 var ns: NativeString
1183
1184 # Current position in `ns`.
1185 var pns: Int
1186
1187 redef var index
1188
1189 # Init the iterator from a RopeBuffer.
1190 init(tgt: RopeBuffer) is old_style_init do
1191 sit = tgt.str.bytes.reverse_iterator
1192 pns = tgt.rpos - 1
1193 index = tgt._bytelen - 1
1194 ns = tgt.ns
1195 end
1196
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
1201 index = pos
1202 ns = tgt.ns
1203 end
1204
1205 redef fun is_ok do return index >= 0
1206
1207 redef fun item do
1208 if pns >= 0 then return ns[pns]
1209 return sit.item
1210 end
1211
1212 redef fun next do
1213 index -= 1
1214 if pns > 0 then
1215 pns -= 1
1216 else
1217 sit.next
1218 end
1219 end
1220 end
1221
1222 # View on the chars of a `RopeBuffer`
1223 class RopeBufferBytes
1224 super BufferByteView
1225
1226 redef type SELFTYPE: RopeBuffer
1227
1228 redef fun [](i) do
1229 if i < target.str.bytelen then
1230 return target.str.bytes[i]
1231 else
1232 return target.ns[i - target.str.bytelen]
1233 end
1234 end
1235
1236 redef fun iterator_from(i) do return new RopeBufferByteIterator.from(target, i)
1237
1238 redef fun reverse_iterator_from(i) do return new RopeBufferByteReverseIterator.from(target, i)
1239 end