66e7734c82f43ac148dee95b4caad4cff7224a24
[nit.git] / lib / standard / text / flat.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it is you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or is a part of
9 # another product.
10
11 # All the array-based text representations
12 module flat
13
14 intrude import abstract_text
15
16 `{
17 #include <stdio.h>
18 #include <string.h>
19 `}
20
21 private class FlatSubstringsIter
22 super Iterator[FlatText]
23
24 var tgt: nullable FlatText
25
26 redef fun item do
27 assert is_ok
28 return tgt.as(not null)
29 end
30
31 redef fun is_ok do return tgt != null
32
33 redef fun next do tgt = null
34 end
35
36 # Immutable strings of characters.
37 class FlatString
38 super FlatText
39 super String
40
41 # Index in _items of the start of the string
42 private var index_from: Int is noinit
43
44 # Indes in _items of the last item of the string
45 private var index_to: Int is noinit
46
47 redef var chars = new FlatStringCharView(self) is lazy
48
49 redef var bytes = new FlatStringByteView(self) is lazy
50
51 redef fun [](index)
52 do
53 # Check that the index (+ index_from) is not larger than indexTo
54 # In other terms, if the index is valid
55 assert index >= 0
56 assert (index + index_from) <= index_to
57 return items[index + index_from].to_i.ascii
58 end
59
60 ################################################
61 # AbstractString specific methods #
62 ################################################
63
64 redef fun reversed
65 do
66 var b = new FlatBuffer.with_capacity(bytelen + 1)
67 for i in [length - 1 .. 0].step(-1) do
68 b.add self[i]
69 end
70 var s = b.to_s.as(FlatString)
71 s.length = self.length
72 return s
73 end
74
75 redef fun fast_cstring do return items.fast_cstring(index_from)
76
77 redef fun substring(from, count)
78 do
79 assert count >= 0
80
81 if from < 0 then
82 count += from
83 if count < 0 then count = 0
84 from = 0
85 end
86
87 var new_from = index_from + from
88
89 if (new_from + count) > index_to then
90 var new_len = index_to - new_from + 1
91 if new_len <= 0 then return empty
92 return new FlatString.with_infos(items, new_len, new_from, index_to)
93 end
94
95 if count <= 0 then return empty
96
97 var to = new_from + count - 1
98
99 return new FlatString.with_infos(items, to - new_from + 1, new_from, to)
100 end
101
102 redef fun empty do return "".as(FlatString)
103
104 redef fun to_upper
105 do
106 var outstr = new FlatBuffer.with_capacity(self.bytelen + 1)
107
108 var mylen = length
109 var pos = 0
110
111 while pos < mylen do
112 outstr.add(chars[pos].to_upper)
113 pos += 1
114 end
115
116 return outstr.to_s
117 end
118
119 redef fun to_lower
120 do
121 var outstr = new FlatBuffer.with_capacity(self.bytelen + 1)
122
123 var mylen = length
124 var pos = 0
125
126 while pos < mylen do
127 outstr.add(chars[pos].to_lower)
128 pos += 1
129 end
130
131 return outstr.to_s
132 end
133
134 redef fun output
135 do
136 for i in chars do i.output
137 end
138
139 ##################################################
140 # String Specific Methods #
141 ##################################################
142
143 # Low-level creation of a new string with given data.
144 #
145 # `items` will be used as is, without copy, to retrieve the characters of the string.
146 # Aliasing issues is the responsibility of the caller.
147 private init with_infos(items: NativeString, length: Int, from: Int, to: Int)
148 do
149 self.items = items
150 self.length = length
151 index_from = from
152 index_to = to
153 end
154
155 redef fun to_cstring
156 do
157 if real_items != null then
158 return real_items.as(not null)
159 else
160 var newItems = new NativeString(length + 1)
161 self.items.copy_to(newItems, length, index_from, 0)
162 newItems[length] = 0u8
163 self.real_items = newItems
164 return newItems
165 end
166 end
167
168 redef fun ==(other)
169 do
170 if not other isa FlatString then return super
171
172 if self.object_id == other.object_id then return true
173
174 var my_length = length
175
176 if other.length != my_length then return false
177
178 var my_index = index_from
179 var its_index = other.index_from
180
181 var last_iteration = my_index + my_length
182
183 var itsitems = other.items
184 var myitems = self.items
185
186 while my_index < last_iteration do
187 if myitems[my_index] != itsitems[its_index] then return false
188 my_index += 1
189 its_index += 1
190 end
191
192 return true
193 end
194
195 redef fun <(other)
196 do
197 if not other isa FlatString then return super
198
199 if self.object_id == other.object_id then return false
200
201 var my_curr_char : Char
202 var its_curr_char : Char
203
204 var my_length = self.length
205 var its_length = other.length
206 var max
207
208 if my_length < its_length then
209 max = my_length
210 else
211 max = its_length
212 end
213
214 var my_chars = chars
215 var its_chars = other.chars
216
217 var pos = 0
218 while pos < max do
219 my_curr_char = my_chars[pos]
220 its_curr_char = its_chars[pos]
221
222 if my_curr_char != its_curr_char then
223 if my_curr_char < its_curr_char then return true
224 return false
225 end
226
227 pos += 1
228 end
229
230 return my_length < its_length
231 end
232
233 redef fun +(o) do
234 var s = o.to_s
235 var slen = s.bytelen
236 var mlen = bytelen
237 var nlen = mlen + slen
238 var mits = items
239 var mifrom = first_byte
240 if s isa FlatText then
241 var sits = s.items
242 var sifrom = s.as(FlatString).first_byte
243 var ns = new NativeString(nlen + 1)
244 mits.copy_to(ns, mlen, mifrom, 0)
245 sits.copy_to(ns, slen, sifrom, mlen)
246 return ns.to_s_with_length(nlen)
247 else
248 abort
249 end
250 end
251
252 redef fun *(i) do
253 var mybtlen = bytelen
254 var new_bytelen = mybtlen * i
255 var mylen = length
256 var newlen = mylen * i
257 var ns = new NativeString(new_bytelen + 1)
258 ns[new_bytelen] = 0u8
259 var offset = 0
260 while i > 0 do
261 items.copy_to(ns, bytelen, first_byte, offset)
262 offset += mybtlen
263 i -= 1
264 end
265 return new FlatString.full(ns, new_bytelen, 0, new_bytelen - 1, newlen)
266 end
267
268 redef fun hash
269 do
270 if hash_cache == null then
271 # djb2 hash algorithm
272 var h = 5381
273 var i = index_from
274
275 var myitems = items
276
277 while i <= index_to do
278 h = h.lshift(5) + h + myitems[i].to_i
279 i += 1
280 end
281
282 hash_cache = h
283 end
284
285 return hash_cache.as(not null)
286 end
287
288 redef fun substrings do return new FlatSubstringsIter(self)
289 end
290
291 private class FlatStringCharReverseIterator
292 super IndexedIterator[Char]
293
294 var target: FlatString
295
296 var curr_pos: Int
297
298 init with_pos(tgt: FlatString, pos: Int)
299 do
300 init(tgt, pos)
301 end
302
303 redef fun is_ok do return curr_pos >= 0
304
305 redef fun item do return target[curr_pos]
306
307 redef fun next do curr_pos -= 1
308
309 redef fun index do return curr_pos
310
311 end
312
313 private class FlatStringCharIterator
314 super IndexedIterator[Char]
315
316 var target: FlatString
317
318 var max: Int
319
320 var curr_pos: Int
321
322 init with_pos(tgt: FlatString, pos: Int)
323 do
324 init(tgt, tgt.length - 1, pos)
325 end
326
327 redef fun is_ok do return curr_pos <= max
328
329 redef fun item do return target[curr_pos]
330
331 redef fun next do curr_pos += 1
332
333 redef fun index do return curr_pos
334
335 end
336
337 private class FlatStringCharView
338 super StringCharView
339
340 redef type SELFTYPE: FlatString
341
342 redef fun [](index) do return target[index]
343
344 redef fun iterator_from(start) do return new FlatStringCharIterator.with_pos(target, start)
345
346 redef fun reverse_iterator_from(start) do return new FlatStringCharReverseIterator.with_pos(target, start)
347
348 end
349
350 private class FlatStringByteReverseIterator
351 super IndexedIterator[Byte]
352
353 var target: FlatString
354
355 var target_items: NativeString
356
357 var curr_pos: Int
358
359 init with_pos(tgt: FlatString, pos: Int)
360 do
361 init(tgt, tgt.items, pos + tgt.index_from)
362 end
363
364 redef fun is_ok do return curr_pos >= target.index_from
365
366 redef fun item do return target_items[curr_pos]
367
368 redef fun next do curr_pos -= 1
369
370 redef fun index do return curr_pos - target.index_from
371
372 end
373
374 private class FlatStringByteIterator
375 super IndexedIterator[Byte]
376
377 var target: FlatString
378
379 var target_items: NativeString
380
381 var curr_pos: Int
382
383 init with_pos(tgt: FlatString, pos: Int)
384 do
385 init(tgt, tgt.items, pos + tgt.index_from)
386 end
387
388 redef fun is_ok do return curr_pos <= target.index_to
389
390 redef fun item do return target_items[curr_pos]
391
392 redef fun next do curr_pos += 1
393
394 redef fun index do return curr_pos - target.index_from
395
396 end
397
398 private class FlatStringByteView
399 super StringByteView
400
401 redef type SELFTYPE: FlatString
402
403 redef fun [](index)
404 do
405 # Check that the index (+ index_from) is not larger than indexTo
406 # In other terms, if the index is valid
407 assert index >= 0
408 var target = self.target
409 assert (index + target.index_from) <= target.index_to
410 return target.items[index + target.index_from]
411 end
412
413 redef fun iterator_from(start) do return new FlatStringByteIterator.with_pos(target, start)
414
415 redef fun reverse_iterator_from(start) do return new FlatStringByteReverseIterator.with_pos(target, start)
416
417 end
418
419 redef class Buffer
420 redef new do return new FlatBuffer
421
422 redef new with_cap(i) do return new FlatBuffer.with_capacity(i)
423 end
424
425 # Mutable strings of characters.
426 class FlatBuffer
427 super FlatText
428 super Buffer
429
430 redef var chars: Sequence[Char] = new FlatBufferCharView(self) is lazy
431
432 redef var bytes: Sequence[Byte] = new FlatBufferByteView(self) is lazy
433
434 private var capacity: Int = 0
435
436 redef fun fast_cstring do return items.fast_cstring(0)
437
438 redef fun substrings do return new FlatSubstringsIter(self)
439
440 # Re-copies the `NativeString` into a new one and sets it as the new `Buffer`
441 #
442 # This happens when an operation modifies the current `Buffer` and
443 # the Copy-On-Write flag `written` is set at true.
444 private fun reset do
445 var nns = new NativeString(capacity)
446 items.copy_to(nns, length, 0, 0)
447 items = nns
448 written = false
449 end
450
451 redef fun [](index)
452 do
453 assert index >= 0
454 assert index < length
455 return items[index].to_i.ascii
456 end
457
458 redef fun []=(index, item)
459 do
460 is_dirty = true
461 if index == length then
462 add(item)
463 return
464 end
465 if written then reset
466 assert index >= 0 and index < length
467 items[index] = item.ascii.to_b
468 end
469
470 redef fun add(c)
471 do
472 is_dirty = true
473 if capacity <= length then enlarge(length + 5)
474 items[length] = c.ascii.to_b
475 length += 1
476 end
477
478 private fun add_byte(b: Byte) do
479 is_dirty = true
480 if capacity <= length then enlarge(length + 5)
481 items[bytelen] = b
482 length += 1
483 end
484
485 redef fun clear do
486 is_dirty = true
487 if written then reset
488 length = 0
489 end
490
491 redef fun empty do return new Buffer
492
493 redef fun enlarge(cap)
494 do
495 var c = capacity
496 if cap <= c then return
497 while c <= cap do c = c * 2 + 2
498 # The COW flag can be set at false here, since
499 # it does a copy of the current `Buffer`
500 written = false
501 var a = new NativeString(c+1)
502 if length > 0 then items.copy_to(a, length, 0, 0)
503 items = a
504 capacity = c
505 end
506
507 redef fun to_s
508 do
509 written = true
510 if length == 0 then items = new NativeString(1)
511 return new FlatString.with_infos(items, length, 0, length - 1)
512 end
513
514 redef fun to_cstring
515 do
516 if is_dirty then
517 var new_native = new NativeString(length + 1)
518 new_native[length] = 0u8
519 if length > 0 then items.copy_to(new_native, length, 0, 0)
520 real_items = new_native
521 is_dirty = false
522 end
523 return real_items.as(not null)
524 end
525
526 # Create a new empty string.
527 init do end
528
529 # Low-level creation a new buffer with given data.
530 #
531 # `items` will be used as is, without copy, to store the characters of the buffer.
532 # Aliasing issues is the responsibility of the caller.
533 #
534 # If `items` is shared, `written` should be set to true after the creation
535 # so that a modification will do a copy-on-write.
536 private init with_infos(items: NativeString, capacity, length: Int)
537 do
538 self.items = items
539 self.length = length
540 self.capacity = capacity
541 end
542
543 # Create a new string copied from `s`.
544 init from(s: Text)
545 do
546 capacity = s.length + 1
547 length = s.length
548 items = new NativeString(capacity)
549 if s isa FlatString then
550 s.items.copy_to(items, length, s.index_from, 0)
551 else if s isa FlatBuffer then
552 s.items.copy_to(items, length, 0, 0)
553 else
554 var curr_pos = 0
555 for i in s.bytes do
556 items[curr_pos] = i
557 curr_pos += 1
558 end
559 end
560 end
561
562 # Create a new empty string with a given capacity.
563 init with_capacity(cap: Int)
564 do
565 assert cap >= 0
566 items = new NativeString(cap+1)
567 capacity = cap
568 length = 0
569 end
570
571 redef fun append(s)
572 do
573 if s.is_empty then return
574 is_dirty = true
575 var sl = s.length
576 if capacity < length + sl then enlarge(length + sl)
577 if s isa FlatString then
578 s.items.copy_to(items, sl, s.index_from, length)
579 else if s isa FlatBuffer then
580 s.items.copy_to(items, sl, 0, length)
581 else
582 var curr_pos = self.length
583 for i in s.bytes do
584 items[curr_pos] = i
585 curr_pos += 1
586 end
587 end
588 length += sl
589 end
590
591 # Copies the content of self in `dest`
592 fun copy(start: Int, len: Int, dest: Buffer, new_start: Int)
593 do
594 var self_chars = self.chars
595 var dest_chars = dest.chars
596 for i in [0..len-1] do
597 dest_chars[new_start+i] = self_chars[start+i]
598 end
599 end
600
601 redef fun substring(from, count)
602 do
603 assert count >= 0
604 count += from
605 if from < 0 then from = 0
606 if count > length then count = length
607 if from < count then
608 var len = count - from
609 var r_items = new NativeString(len)
610 items.copy_to(r_items, len, from, 0)
611 var r = new FlatBuffer.with_infos(r_items, len, len)
612 return r
613 else
614 return new Buffer
615 end
616 end
617
618 redef fun reverse
619 do
620 written = false
621 var ns = new NativeString(capacity)
622 var si = length - 1
623 var ni = 0
624 var it = items
625 while si >= 0 do
626 ns[ni] = it[si]
627 ni += 1
628 si -= 1
629 end
630 items = ns
631 end
632
633 redef fun times(repeats)
634 do
635 var x = new FlatString.with_infos(items, length, 0, length - 1)
636 for i in [1..repeats[ do
637 append(x)
638 end
639 end
640
641 redef fun upper
642 do
643 if written then reset
644 var id = length - 1
645 while id >= 0 do
646 self[id] = self[id].to_upper
647 id -= 1
648 end
649 end
650
651 redef fun lower
652 do
653 if written then reset
654 var id = length - 1
655 while id >= 0 do
656 self[id] = self[id].to_lower
657 id -= 1
658 end
659 end
660 end
661
662 private class FlatBufferByteReverseIterator
663 super IndexedIterator[Byte]
664
665 var target: FlatBuffer
666
667 var target_items: NativeString
668
669 var curr_pos: Int
670
671 init with_pos(tgt: FlatBuffer, pos: Int)
672 do
673 init(tgt, tgt.items, pos)
674 end
675
676 redef fun index do return curr_pos
677
678 redef fun is_ok do return curr_pos >= 0
679
680 redef fun item do return target_items[curr_pos]
681
682 redef fun next do curr_pos -= 1
683
684 end
685
686 private class FlatBufferByteView
687 super BufferByteView
688
689 redef type SELFTYPE: FlatBuffer
690
691 redef fun [](index) do return target.items[index]
692
693 redef fun []=(index, item)
694 do
695 assert index >= 0 and index <= target.bytelen
696 if index == target.bytelen then
697 add(item)
698 return
699 end
700 target.items[index] = item
701 end
702
703 redef fun push(c)
704 do
705 target.add_byte(c)
706 end
707
708 fun enlarge(cap: Int)
709 do
710 target.enlarge(cap)
711 end
712
713 redef fun append(s)
714 do
715 var s_length = s.length
716 if target.capacity < (target.length + s_length) then enlarge(s_length + target.length)
717 var pos = target.length
718 var its = target.items
719 for i in s do
720 its[pos] = i
721 pos += 1
722 end
723 target.length += s.length
724 end
725
726 redef fun iterator_from(pos) do return new FlatBufferByteIterator.with_pos(target, pos)
727
728 redef fun reverse_iterator_from(pos) do return new FlatBufferByteReverseIterator.with_pos(target, pos)
729
730 end
731
732 private class FlatBufferByteIterator
733 super IndexedIterator[Byte]
734
735 var target: FlatBuffer
736
737 var target_items: NativeString
738
739 var curr_pos: Int
740
741 init with_pos(tgt: FlatBuffer, pos: Int)
742 do
743 init(tgt, tgt.items, pos)
744 end
745
746 redef fun index do return curr_pos
747
748 redef fun is_ok do return curr_pos < target.length
749
750 redef fun item do return target_items[curr_pos]
751
752 redef fun next do curr_pos += 1
753
754 end
755
756 private class FlatBufferCharReverseIterator
757 super IndexedIterator[Char]
758
759 var target: FlatBuffer
760
761 var curr_pos: Int
762
763 init with_pos(tgt: FlatBuffer, pos: Int)
764 do
765 init(tgt, pos)
766 end
767
768 redef fun index do return curr_pos
769
770 redef fun is_ok do return curr_pos >= 0
771
772 redef fun item do return target[curr_pos]
773
774 redef fun next do curr_pos -= 1
775
776 end
777
778 private class FlatBufferCharView
779 super BufferCharView
780
781 redef type SELFTYPE: FlatBuffer
782
783 redef fun [](index) do return target[index]
784
785 redef fun []=(index, item)
786 do
787 assert index >= 0 and index <= length
788 if index == length then
789 add(item)
790 return
791 end
792 target[index] = item
793 end
794
795 redef fun push(c)
796 do
797 target.add(c)
798 end
799
800 redef fun add(c)
801 do
802 target.add(c)
803 end
804
805 fun enlarge(cap: Int)
806 do
807 target.enlarge(cap)
808 end
809
810 redef fun append(s)
811 do
812 var s_length = s.length
813 if target.capacity < s.length then enlarge(s_length + target.length)
814 for i in s do target.add i
815 end
816
817 redef fun iterator_from(pos) do return new FlatBufferCharIterator.with_pos(target, pos)
818
819 redef fun reverse_iterator_from(pos) do return new FlatBufferCharReverseIterator.with_pos(target, pos)
820
821 end
822
823 private class FlatBufferCharIterator
824 super IndexedIterator[Char]
825
826 var target: FlatBuffer
827
828 var max: Int
829
830 var curr_pos: Int
831
832 init with_pos(tgt: FlatBuffer, pos: Int)
833 do
834 init(tgt, tgt.length - 1, pos)
835 end
836
837 redef fun index do return curr_pos
838
839 redef fun is_ok do return curr_pos <= max
840
841 redef fun item do return target[curr_pos]
842
843 redef fun next do curr_pos += 1
844
845 end
846
847 redef class NativeString
848 redef fun to_s
849 do
850 return to_s_with_length(cstring_length)
851 end
852
853 # Returns `self` as a String of `length`.
854 redef fun to_s_with_length(length): FlatString
855 do
856 assert length >= 0
857 var str = new FlatString.with_infos(self, length, 0, length - 1)
858 return str
859 end
860
861 # Returns `self` as a new String.
862 redef fun to_s_with_copy: FlatString
863 do
864 var length = cstring_length
865 var new_self = new NativeString(length + 1)
866 copy_to(new_self, length, 0, 0)
867 var str = new FlatString.with_infos(new_self, length, 0, length - 1)
868 new_self[length] = 0u8
869 str.real_items = new_self
870 return str
871 end
872
873 # Sets the next bytes at position `pos` to the value of `c`, encoded in UTF-8
874 #
875 # Very unsafe, make sure to have room for this char prior to calling this function.
876 private fun set_char_at(pos: Int, c: Char) do
877 var ln = c.u8char_len
878 native_set_char(pos, c, ln)
879 end
880
881 private fun native_set_char(pos: Int, c: Char, ln: Int) `{
882 char* dst = self + pos;
883 switch(ln){
884 case 1:
885 dst[0] = c;
886 break;
887 case 2:
888 dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
889 dst[1] = 0x80 | (c & 0x3F);
890 break;
891 case 3:
892 dst[0] = 0xE0 | ((c & 0xF000) >> 12);
893 dst[1] = 0x80 | ((c & 0xFC0) >> 6);
894 dst[2] = 0x80 | (c & 0x3F);
895 break;
896 case 4:
897 dst[0] = 0xF0 | ((c & 0x1C0000) >> 18);
898 dst[1] = 0x80 | ((c & 0x3F000) >> 12);
899 dst[2] = 0x80 | ((c & 0xFC0) >> 6);
900 dst[3] = 0x80 | (c & 0x3F);
901 break;
902 }
903 `}
904 end
905
906 redef class Int
907 redef fun to_base(base, signed)
908 do
909 var l = digit_count(base)
910 var s = new FlatBuffer.from(" " * l)
911 fill_buffer(s, base, signed)
912 return s.to_s
913 end
914
915 # return displayable int in base 10 and signed
916 #
917 # assert 1.to_s == "1"
918 # assert (-123).to_s == "-123"
919 redef fun to_s do
920 # Fast case for common numbers
921 if self == 0 then return "0"
922 if self == 1 then return "1"
923
924 var nslen = int_to_s_len
925 var ns = new NativeString(nslen + 1)
926 ns[nslen] = 0u8
927 native_int_to_s(ns, nslen + 1)
928 return ns.to_s_with_length(nslen)
929 end
930 end
931
932 redef class Array[E]
933
934 # Fast implementation
935 redef fun plain_to_s
936 do
937 var l = length
938 if l == 0 then return ""
939 if l == 1 then if self[0] == null then return "" else return self[0].to_s
940 var its = _items
941 var na = new NativeArray[String](l)
942 var i = 0
943 var sl = 0
944 var mypos = 0
945 while i < l do
946 var itsi = its[i]
947 if itsi == null then
948 i += 1
949 continue
950 end
951 var tmp = itsi.to_s
952 sl += tmp.length
953 na[mypos] = tmp
954 i += 1
955 mypos += 1
956 end
957 var ns = new NativeString(sl + 1)
958 ns[sl] = 0u8
959 i = 0
960 var off = 0
961 while i < mypos do
962 var tmp = na[i]
963 var tpl = tmp.length
964 if tmp isa FlatString then
965 tmp.items.copy_to(ns, tpl, tmp.index_from, off)
966 off += tpl
967 else
968 for j in tmp.substrings do
969 var s = j.as(FlatString)
970 var slen = s.length
971 s.items.copy_to(ns, slen, s.index_from, off)
972 off += slen
973 end
974 end
975 i += 1
976 end
977 return ns.to_s_with_length(sl)
978 end
979 end
980
981 redef class NativeArray[E]
982 redef fun native_to_s do
983 assert self isa NativeArray[String]
984 var l = length
985 var na = self
986 var i = 0
987 var sl = 0
988 var mypos = 0
989 while i < l do
990 sl += na[i].length
991 i += 1
992 mypos += 1
993 end
994 var ns = new NativeString(sl + 1)
995 ns[sl] = 0u8
996 i = 0
997 var off = 0
998 while i < mypos do
999 var tmp = na[i]
1000 var tpl = tmp.length
1001 if tmp isa FlatString then
1002 tmp.items.copy_to(ns, tpl, tmp.index_from, off)
1003 off += tpl
1004 else
1005 for j in tmp.substrings do
1006 var s = j.as(FlatString)
1007 var slen = s.length
1008 s.items.copy_to(ns, slen, s.index_from, off)
1009 off += slen
1010 end
1011 end
1012 i += 1
1013 end
1014 return ns.to_s_with_length(sl)
1015 end
1016 end
1017
1018 redef class Map[K,V]
1019 redef fun join(sep, couple_sep)
1020 do
1021 if is_empty then return ""
1022
1023 var s = new Buffer # Result
1024
1025 # Concat first item
1026 var i = iterator
1027 var k = i.key
1028 var e = i.item
1029 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1030
1031 # Concat other items
1032 i.next
1033 while i.is_ok do
1034 s.append(sep)
1035 k = i.key
1036 e = i.item
1037 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1038 i.next
1039 end
1040 return s.to_s
1041 end
1042 end