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