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