lib/standard: Update libs for the support of UTF-8
[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.lshift(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: Sequence[Byte] = new FlatBufferByteView(self) is lazy
475
476 redef var bytelen = 0
477
478 # O(n)
479 redef fun length do
480 var max = bytelen
481 if max == 0 then return 0
482 var pos = 0
483 var ln = 0
484 var its = items
485 while pos < max do
486 pos += its.length_of_char_at(pos)
487 ln += 1
488 end
489 return ln
490 end
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 [](i)
531 do
532 assert i < length and i >= 0
533 return items.char_at(items.char_to_byte_index(i))
534 end
535
536 redef fun []=(index, item)
537 do
538 assert index >= 0 and index <= length
539 if written then reset
540 is_dirty = true
541 if index == length then
542 add item
543 return
544 end
545 var ip = items.char_to_byte_index(index)
546 var c = items.char_at(ip)
547 var clen = c.u8char_len
548 var itemlen = item.u8char_len
549 var size_diff = itemlen - clen
550 if size_diff > 0 then
551 rshift_bytes(ip + clen, size_diff)
552 else if size_diff < 0 then
553 lshift_bytes(ip + clen, -size_diff)
554 end
555 bytelen += size_diff
556 items.set_char_at(ip, item)
557 end
558
559 redef fun add(c)
560 do
561 if written then reset
562 is_dirty = true
563 var clen = c.u8char_len
564 enlarge(bytelen + clen)
565 items.set_char_at(bytelen, c)
566 bytelen += clen
567 end
568
569 private fun add_byte(b: Byte) do
570 if written then reset
571 is_dirty = true
572 enlarge(bytelen + 1)
573 items[bytelen] = b
574 # FIXME: Might trigger errors
575 bytelen += 1
576 end
577
578 redef fun clear do
579 is_dirty = true
580 if written then reset
581 bytelen = 0
582 end
583
584 redef fun empty do return new Buffer
585
586 redef fun enlarge(cap)
587 do
588 var c = capacity
589 if cap <= c then return
590 while c <= cap do c = c * 2 + 2
591 # The COW flag can be set at false here, since
592 # it does a copy of the current `Buffer`
593 written = false
594 var a = new NativeString(c+1)
595 if bytelen > 0 then items.copy_to(a, bytelen, 0, 0)
596 items = a
597 capacity = c
598 end
599
600 redef fun to_s
601 do
602 written = true
603 if bytelen == 0 then items = new NativeString(1)
604 return new FlatString.with_infos(items, bytelen, 0, bytelen - 1)
605 end
606
607 redef fun to_cstring
608 do
609 if is_dirty then
610 var new_native = new NativeString(bytelen + 1)
611 new_native[bytelen] = 0u8
612 if length > 0 then items.copy_to(new_native, bytelen, 0, 0)
613 real_items = new_native
614 is_dirty = false
615 end
616 return real_items.as(not null)
617 end
618
619 # Create a new empty string.
620 init do end
621
622 # Low-level creation a new buffer with given data.
623 #
624 # `items` will be used as is, without copy, to store the characters of the buffer.
625 # Aliasing issues is the responsibility of the caller.
626 #
627 # If `items` is shared, `written` should be set to true after the creation
628 # so that a modification will do a copy-on-write.
629 private init with_infos(items: NativeString, capacity, bytelen: Int)
630 do
631 self.items = items
632 self.capacity = capacity
633 self.bytelen = bytelen
634 end
635
636 # Create a new string copied from `s`.
637 init from(s: Text)
638 do
639 items = new NativeString(s.bytelen)
640 if s isa FlatText then
641 items = s.items
642 else
643 for i in substrings do i.as(FlatString).items.copy_to(items, i.bytelen, 0, 0)
644 end
645 bytelen = s.bytelen
646 capacity = s.bytelen
647 written = true
648 end
649
650 # Create a new empty string with a given capacity.
651 init with_capacity(cap: Int)
652 do
653 assert cap >= 0
654 items = new NativeString(cap + 1)
655 capacity = cap
656 bytelen = 0
657 end
658
659 redef fun append(s)
660 do
661 if s.is_empty then return
662 is_dirty = true
663 var sl = s.bytelen
664 enlarge(bytelen + sl)
665 if s isa FlatString then
666 s.items.copy_to(items, sl, s.first_byte, bytelen)
667 else if s isa FlatBuffer then
668 s.items.copy_to(items, sl, 0, bytelen)
669 else
670 for i in s.substrings do append i
671 return
672 end
673 bytelen += sl
674 end
675
676 # Copies the content of self in `dest`
677 fun copy(start: Int, len: Int, dest: Buffer, new_start: Int)
678 do
679 var self_chars = self.chars
680 var dest_chars = dest.chars
681 for i in [0..len-1] do
682 dest_chars[new_start+i] = self_chars[start+i]
683 end
684 end
685
686 redef fun substring(from, count)
687 do
688 assert count >= 0
689 if from < 0 then from = 0
690 if (from + count) > length then count = length - from
691 if count != 0 then
692 var bytefrom = items.char_to_byte_index(from)
693 var byteto = items.char_to_byte_index(count + from - 1)
694 byteto += items.char_at(byteto).u8char_len - 1
695 var byte_length = byteto - bytefrom + 1
696 var r_items = new NativeString(byte_length)
697 items.copy_to(r_items, byte_length, bytefrom, 0)
698 return new FlatBuffer.with_infos(r_items, byte_length, byte_length)
699 else
700 return new Buffer
701 end
702 end
703
704 redef fun reverse
705 do
706 written = false
707 var ns = new FlatBuffer.with_capacity(capacity)
708 for i in chars.reverse_iterator do ns.add i
709 items = ns.items
710 end
711
712 redef fun times(repeats)
713 do
714 var x = new FlatString.with_infos(items, bytelen, 0, bytelen - 1)
715 for i in [1 .. repeats[ do
716 append(x)
717 end
718 end
719
720 redef fun upper
721 do
722 if written then reset
723 for i in [0 .. length[ do self[i] = self[i].to_upper
724 end
725
726 redef fun lower
727 do
728 if written then reset
729 for i in [0 .. length[ do self[i] = self[i].to_lower
730 end
731 end
732
733 private class FlatBufferByteReverseIterator
734 super IndexedIterator[Byte]
735
736 var target: FlatBuffer
737
738 var target_items: NativeString
739
740 var curr_pos: Int
741
742 init with_pos(tgt: FlatBuffer, pos: Int)
743 do
744 init(tgt, tgt.items, pos)
745 end
746
747 redef fun index do return curr_pos
748
749 redef fun is_ok do return curr_pos >= 0
750
751 redef fun item do return target_items[curr_pos]
752
753 redef fun next do curr_pos -= 1
754
755 end
756
757 private class FlatBufferByteView
758 super BufferByteView
759
760 redef type SELFTYPE: FlatBuffer
761
762 redef fun [](index) do return target.items[index]
763
764 redef fun []=(index, item)
765 do
766 assert index >= 0 and index <= target.bytelen
767 if index == target.bytelen then
768 add(item)
769 return
770 end
771 target.items[index] = item
772 end
773
774 redef fun push(c)
775 do
776 target.add_byte(c)
777 end
778
779 fun enlarge(cap: Int)
780 do
781 target.enlarge(cap)
782 end
783
784 redef fun append(s)
785 do
786 var s_length = s.length
787 if target.capacity < (target.length + s_length) then enlarge(s_length + target.length)
788 var pos = target.length
789 var its = target.items
790 for i in s do
791 its[pos] = i
792 pos += 1
793 end
794 target.length += s.length
795 end
796
797 redef fun iterator_from(pos) do return new FlatBufferByteIterator.with_pos(target, pos)
798
799 redef fun reverse_iterator_from(pos) do return new FlatBufferByteReverseIterator.with_pos(target, pos)
800
801 end
802
803 private class FlatBufferByteIterator
804 super IndexedIterator[Byte]
805
806 var target: FlatBuffer
807
808 var target_items: NativeString
809
810 var curr_pos: Int
811
812 init with_pos(tgt: FlatBuffer, pos: Int)
813 do
814 init(tgt, tgt.items, pos)
815 end
816
817 redef fun index do return curr_pos
818
819 redef fun is_ok do return curr_pos < target.bytelen
820
821 redef fun item do return target_items[curr_pos]
822
823 redef fun next do curr_pos += 1
824
825 end
826
827 private class FlatBufferCharReverseIterator
828 super IndexedIterator[Char]
829
830 var target: FlatBuffer
831
832 var curr_pos: Int
833
834 init with_pos(tgt: FlatBuffer, pos: Int)
835 do
836 init(tgt, pos)
837 end
838
839 redef fun index do return curr_pos
840
841 redef fun is_ok do return curr_pos >= 0
842
843 redef fun item do return target[curr_pos]
844
845 redef fun next do curr_pos -= 1
846
847 end
848
849 private class FlatBufferCharView
850 super BufferCharView
851
852 redef type SELFTYPE: FlatBuffer
853
854 redef fun [](index) do return target[index]
855
856 redef fun []=(index, item)
857 do
858 assert index >= 0 and index <= length
859 if index == length then
860 add(item)
861 return
862 end
863 target[index] = item
864 end
865
866 redef fun push(c)
867 do
868 target.add(c)
869 end
870
871 redef fun add(c)
872 do
873 target.add(c)
874 end
875
876 fun enlarge(cap: Int)
877 do
878 target.enlarge(cap)
879 end
880
881 redef fun append(s)
882 do
883 var s_length = s.length
884 if target.capacity < s.length then enlarge(s_length + target.length)
885 for i in s do target.add i
886 end
887
888 redef fun iterator_from(pos) do return new FlatBufferCharIterator.with_pos(target, pos)
889
890 redef fun reverse_iterator_from(pos) do return new FlatBufferCharReverseIterator.with_pos(target, pos)
891
892 end
893
894 private class FlatBufferCharIterator
895 super IndexedIterator[Char]
896
897 var target: FlatBuffer
898
899 var max: Int
900
901 var curr_pos: Int
902
903 init with_pos(tgt: FlatBuffer, pos: Int)
904 do
905 init(tgt, tgt.length - 1, pos)
906 end
907
908 redef fun index do return curr_pos
909
910 redef fun is_ok do return curr_pos <= max
911
912 redef fun item do return target[curr_pos]
913
914 redef fun next do curr_pos += 1
915
916 end
917
918 redef class NativeString
919 redef fun to_s
920 do
921 return to_s_with_length(cstring_length)
922 end
923
924 # Returns `self` as a String of `length`.
925 redef fun to_s_with_length(length): FlatString
926 do
927 assert length >= 0
928 var str = new FlatString.with_infos(self, length, 0, length - 1)
929 return str
930 end
931
932 # Returns `self` as a new String.
933 redef fun to_s_with_copy: FlatString
934 do
935 var length = cstring_length
936 var new_self = new NativeString(length + 1)
937 copy_to(new_self, length, 0, 0)
938 var str = new FlatString.with_infos(new_self, length, 0, length - 1)
939 new_self[length] = 0u8
940 str.real_items = new_self
941 return str
942 end
943
944 # Sets the next bytes at position `pos` to the value of `c`, encoded in UTF-8
945 #
946 # Very unsafe, make sure to have room for this char prior to calling this function.
947 private fun set_char_at(pos: Int, c: Char) do
948 var ln = c.u8char_len
949 native_set_char(pos, c, ln)
950 end
951
952 private fun native_set_char(pos: Int, c: Char, ln: Int) `{
953 char* dst = self + pos;
954 switch(ln){
955 case 1:
956 dst[0] = c;
957 break;
958 case 2:
959 dst[0] = 0xC0 | ((c & 0x7C0) >> 6);
960 dst[1] = 0x80 | (c & 0x3F);
961 break;
962 case 3:
963 dst[0] = 0xE0 | ((c & 0xF000) >> 12);
964 dst[1] = 0x80 | ((c & 0xFC0) >> 6);
965 dst[2] = 0x80 | (c & 0x3F);
966 break;
967 case 4:
968 dst[0] = 0xF0 | ((c & 0x1C0000) >> 18);
969 dst[1] = 0x80 | ((c & 0x3F000) >> 12);
970 dst[2] = 0x80 | ((c & 0xFC0) >> 6);
971 dst[3] = 0x80 | (c & 0x3F);
972 break;
973 }
974 `}
975 end
976
977 redef class Int
978 redef fun to_base(base, signed)
979 do
980 var l = digit_count(base)
981 var s = new FlatBuffer.from(" " * l)
982 fill_buffer(s, base, signed)
983 return s.to_s
984 end
985
986 # return displayable int in base 10 and signed
987 #
988 # assert 1.to_s == "1"
989 # assert (-123).to_s == "-123"
990 redef fun to_s do
991 # Fast case for common numbers
992 if self == 0 then return "0"
993 if self == 1 then return "1"
994
995 var nslen = int_to_s_len
996 var ns = new NativeString(nslen + 1)
997 ns[nslen] = 0u8
998 native_int_to_s(ns, nslen + 1)
999 return new FlatString.full(ns, nslen, 0, nslen - 1, nslen)
1000 end
1001 end
1002
1003 redef class Array[E]
1004
1005 # Fast implementation
1006 redef fun plain_to_s
1007 do
1008 var l = length
1009 if l == 0 then return ""
1010 if l == 1 then if self[0] == null then return "" else return self[0].to_s
1011 var its = _items
1012 var na = new NativeArray[String](l)
1013 var i = 0
1014 var sl = 0
1015 var mypos = 0
1016 while i < l do
1017 var itsi = its[i]
1018 if itsi == null then
1019 i += 1
1020 continue
1021 end
1022 var tmp = itsi.to_s
1023 sl += tmp.bytelen
1024 na[mypos] = tmp
1025 i += 1
1026 mypos += 1
1027 end
1028 var ns = new NativeString(sl + 1)
1029 ns[sl] = 0u8
1030 i = 0
1031 var off = 0
1032 while i < mypos do
1033 var tmp = na[i]
1034 if tmp isa FlatString then
1035 var tpl = tmp.bytelen
1036 tmp.items.copy_to(ns, tpl, tmp.first_byte, off)
1037 off += tpl
1038 else
1039 for j in tmp.substrings do
1040 var s = j.as(FlatString)
1041 var slen = s.bytelen
1042 s.items.copy_to(ns, slen, s.first_byte, off)
1043 off += slen
1044 end
1045 end
1046 i += 1
1047 end
1048 return ns.to_s_with_length(sl)
1049 end
1050 end
1051
1052 redef class NativeArray[E]
1053 redef fun native_to_s do
1054 assert self isa NativeArray[String]
1055 var l = length
1056 var na = self
1057 var i = 0
1058 var sl = 0
1059 var mypos = 0
1060 while i < l do
1061 sl += na[i].bytelen
1062 i += 1
1063 mypos += 1
1064 end
1065 var ns = new NativeString(sl + 1)
1066 ns[sl] = 0u8
1067 i = 0
1068 var off = 0
1069 while i < mypos do
1070 var tmp = na[i]
1071 if tmp isa FlatString then
1072 var tpl = tmp.bytelen
1073 tmp.items.copy_to(ns, tpl, tmp.first_byte, off)
1074 off += tpl
1075 else
1076 for j in tmp.substrings do
1077 var s = j.as(FlatString)
1078 var slen = s.bytelen
1079 s.items.copy_to(ns, slen, s.first_byte, off)
1080 off += slen
1081 end
1082 end
1083 i += 1
1084 end
1085 return ns.to_s_with_length(sl)
1086 end
1087 end
1088
1089 redef class Map[K,V]
1090 redef fun join(sep, couple_sep)
1091 do
1092 if is_empty then return ""
1093
1094 var s = new Buffer # Result
1095
1096 # Concat first item
1097 var i = iterator
1098 var k = i.key
1099 var e = i.item
1100 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1101
1102 # Concat other items
1103 i.next
1104 while i.is_ok do
1105 s.append(sep)
1106 k = i.key
1107 e = i.item
1108 s.append("{k or else "<null>"}{couple_sep}{e or else "<null>"}")
1109 i.next
1110 end
1111 return s.to_s
1112 end
1113 end