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