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